/PROG/ Python scopes
Aug. 27th, 2006 11:30 amЕсли Питон вымрет, то самым толстым гвоздём в крышку гроба будет не система отступов, к которой привыкнуть - дело пары часов, не динамическая типизация и не отсутствие ограничения видимости при инкапсуляции, а клинически неестественная схема видимости переменных.
Простейший пример:
Оно отвечает:
А теперь если убрать назначение p всё будет работать:
Но если мы после 'print p' добавим инкремент - всё снова сломается, и не выполнится даже print:
будет ошибка дословно повторяющая первый случай, 'print p' не сработает.
Смотрим как это сказано в описании языка (пункт 4.1, 'Naming and binding'):
Each occurrence of a name in the program text refers to the binding of that name established in the innermost function block containing the use.
Ну да, если внимательно вчитаться (и с поправкой далее сказанной очень туманно) что function block это и уровень модуля - да, поведение соответствует описанию. Только вопрос - из какой такой логики переменные можно читать (а заодно вызывать в них методы, и вообще вытворять всё что угодно), но нельзя делать только одно - присвоение?
Как для меня вывод однозначен - результат бессмысленной экономии на (в терминах перла) my и local.
global в использующем блоке это лечит (thx2 ormuz, я как-то пропустил из-за чрезмерной лаконичности документации). Но только по отношению к глобальным переменным (тем что в globals). И логика этого действия противоположна тому что принимается в более нормальных средствах - когда надо явно объявить переменную какая она (local, global, из внешнего блока) при жёстком контроле. Проверки компилятора и pychecker'а слабоваты.
Более скрытая вариация на ту же тему:
Запустив получаем:
Опять g1 оказалось локальным только потому что ему есть хотя бы одно присвоение. 'global g1' не помогло.
UPDATED (12:56) UPDATED(28.08 18:16)
Простейший пример:
def main():
p += 1
if __name__ == '__main__':
p = 0
main()
Оно отвечает:
Traceback (most recent call last):
File "1.py", line 6, in ?
main()
File "1.py", line 3, in main
p += 1
UnboundLocalError: local variable 'p' referenced before assignment
А теперь если убрать назначение p всё будет работать:
def main():
print p
if __name__ == '__main__':
p = 0
main()
Но если мы после 'print p' добавим инкремент - всё снова сломается, и не выполнится даже print:
def main():
print p
p += 1
if __name__ == '__main__':
p = 0
main()
будет ошибка дословно повторяющая первый случай, 'print p' не сработает.
Смотрим как это сказано в описании языка (пункт 4.1, 'Naming and binding'):
Each occurrence of a name in the program text refers to the binding of that name established in the innermost function block containing the use.
Ну да, если внимательно вчитаться (и с поправкой далее сказанной очень туманно) что function block это и уровень модуля - да, поведение соответствует описанию. Только вопрос - из какой такой логики переменные можно читать (а заодно вызывать в них методы, и вообще вытворять всё что угодно), но нельзя делать только одно - присвоение?
Как для меня вывод однозначен - результат бессмысленной экономии на (в терминах перла) my и local.
global в использующем блоке это лечит (thx2 ormuz, я как-то пропустил из-за чрезмерной лаконичности документации). Но только по отношению к глобальным переменным (тем что в globals). И логика этого действия противоположна тому что принимается в более нормальных средствах - когда надо явно объявить переменную какая она (local, global, из внешнего блока) при жёстком контроле. Проверки компилятора и pychecker'а слабоваты.
Более скрытая вариация на ту же тему:
$ tail +1 main.py A.py
==> main.py <==
#!/usr/bin/env python
import A
from A import g1
if __name__ == '__main__':
g1 = {}
print id(g1), A.f1()
A.g1 = g1
print id(g1), A.f1()
==> A.py <==
## module A
g1 = None
def f1():
return id(g1)
Запустив получаем:
136034476 135227424
136034476 136034476
Опять g1 оказалось локальным только потому что ему есть хотя бы одно присвоение. 'global g1' не помогло.
UPDATED (12:56) UPDATED(28.08 18:16)
no subject
Date: 2006-08-28 10:10 am (UTC)def hehe(smiles=[]):
smiles.append(':)')
print " ".join(smiles)
hehe()
hehe()
hehe(["8-o"])
hehe()
просто он другой...
все привыкли декларировать локльные переменные явно, а из вышестоящего импортировать по дефолту
тут же на оборот локальные выделяются по умолчанию в случае присваивания, что очень удобно для некоторых (покрайней я за собой часто замечал постоянные декларации всяческих итераторов и временных)
по поводу управления доступом - это реализуется с помощью имен (аля __private - что вызовет пробразование в ____private или что-то в этом роде)
>...Только вопрос - из какой такой логики переменные можно читать (а заодно вызывать в них >методы, и вообще вытворять всё что угодно), но нельзя делать только одно - присвоение?
Немножко идеи: в питоне переменные - это ссылки, когда ты пишешь a = 'x' ты создаешь ссылку на самом ближайшем фрэйме на объект 'x', а обращатся к ней ты можешь откуда угодно если не перекрываешь идентификатор или же не пере определяешь на том же уровне.
А теперь с другой стороны к тому же: все цифры и строки (и прочие) immutable - это не переменные и x += 1 - значит найти сослать идентификатор x на цифру которая является результатом x+1, т.е. сослать на новый константный объект (мне эта идея очень нравится)
Последнее очень очевидное поведение, когда ты выделив в одном модуле ссылку g1 на None (и на том же уровне f1, ссылающуюся на объект который можно вызвать) переопредлил ее.
подобное позволят экономить многие вещи, например можно не делать кучу класов, а сразу на лету подменять методы классов (см. bind)
прим: чаще всего обращение к любому объекту в питоне делается через obj.attrname.
если целью является бодание с языком, то это все конечно "клинически дурная схема" а не попытка разтормошить закостенелые мозги C++'ников и perl'овиков, ну и других.
кстати, в Tcl'е есть аж три способа связать идентификатор с переменной variable, global, upvar
в perl'е я вообще не помню что бы можно было это делать.
P.S. Эх... Prolog должен войти в школьную программу обучения, причем не так как его преподают в КПИ'ях.