/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 12:53 am (UTC)1) Первое утверждение совершенно неверно. from ... import присваивает имени в текущем контексте значение аналогичного имени в другом контексте. То есть в определенном смысле создает новую переменную.
A.py: g1 = 1 B.py: import A from A import g1 A.g1 = 2 print g1, A.g1 python B.py: 1 2В этом контексте нет никакой разницы между "from A import g1" и "g1 = A.g1". Что я тебе собственно и пытаюсь объяснить.
2) Второе утверждение правильно. Более того, "import" в блоке тоже создает локальные переменные:
A.py: g1 = 1 B.py: def f(): from A import g1 print "in f, g1 =", g1 f() print "outside of f, g1 = ", g1 python B.py: in f, g1 = 1 outside of f, g1 = Traceback (most recent call last): File "B.py", line 6, in ? print "outside of f, g1 = ", g1 NameError: name 'g1' is not definedОпять же, это все очень хорошо описано в 4.1 Naming and binding и PEP 227 - Statically Nested Scopes.
Поэтому вопрос к тебе: в чем собственно состоит "клиническая дурость" видимости имен в Питоне (если прочитать документацию)?
no subject
Date: 2006-08-28 05:22 am (UTC)Убил.
> Поэтому вопрос к тебе: в чем собственно состоит "клиническая дурость" видимости имен в Питоне (если прочитать документацию)?
Давай я тебе отвечу вопросом на вопрос: чем такая схема принципиально лучше схемы в стиле, например, перла с его my, local, use vars и теми ограничениями которые накладываются при -Mstrict?
no subject
Date: 2006-08-28 10:47 am (UTC)no subject
Date: 2006-08-28 11:05 am (UTC)class mumap: def __init__(self, proc, seq): self.__proc = proc self.__seq = seq self.__len__ = seq.__len__ def __contains__(self, y): for val in self.__seq: if self.__proc(val) == y: return True return False def __eq__(self, y): if len(self) != len(y): return False i = iter(self.__seq) j = iter(y) try: if self._proc(i.next()) != j.next(): return False except StopIteration: return True def __getitem__(self, y): return self.__proc(self.__seq[y]) def __getslice__(self, i, j): return mumap(self.__proc, self.__seq[i:j]) def __iter__(self): i = iter(self.__seq) while True: yield self.__proc(i.next()) def __str__(self): return ", ".join(self) x = ["a","b"] y = mumap(lambda x: x+"z", x) print y x[0]="g" print yno subject
Date: 2006-08-28 02:49 pm (UTC)Лучше, хуже - в данном случае сугубо вопрос личных стилистических предпочтений. Обе схемы внутренне вполне логичны и даже (my vs. global) в каком-то смысле симметричны. (Встроенного в язык аналога local в питоне вроде как нету?)
no subject
Date: 2006-08-28 03:14 pm (UTC)local - да, нету.
no subject
Date: 2006-08-28 05:39 am (UTC)Впрочем, разница существенна только для элементарных значений. Видимо, поэтому я её не уловил и она мне по сути и не была важна.:))
А вот чем это совсем плохо - незаменой скопированного по import в случае перезагрузки модуля. Надо будет проверить.
no subject
Date: 2006-08-28 06:16 am (UTC)from A import g1
A.g1 = {чтототам}
поставил в голове птичку (v) Питон - НЕ МОДУЛА-2
дальше разбираться нужды пока не было.
no subject
Date: 2006-08-28 06:19 am (UTC)И если будет так что например 'from A import AA' будет держать закэшированным старое определение класса AA - результат будет совсем не тот чем нужный мне:( Похоже, вообще все from-import надо будет убрать подальше...
no subject
Date: 2006-08-28 02:58 pm (UTC)no subject
Date: 2006-08-28 03:15 pm (UTC)Когда решалось что использовать - erlang, питон или ещё что-то - существенным козырем в пользу erlang'а была именно его автоматизированная и удобная схема управления перезагрузкой модулей.
no subject
Date: 2006-08-28 04:43 pm (UTC)Популярность языка программирования, как известно, обеспечивается:
(1) понятностью программ, написанных на этом языке
(2) рекламой языка и культуры
(3) техническими достоинствами
Перл выигрывает по критериям (2) и (3) (но читать код на перле нельзя), питон - (1) и (3) (но рекламы в мейнстриме ему явно не хватает).
no subject
Date: 2006-08-28 04:51 pm (UTC)Ну почему так сразу ужас.
Эриксоновцы померяли, что при набитой руке на нём пишется в разы быстрее чем на всяких сях и даже питонах. Не знаю, что было тут натянуто, но то что не медленнее и не хуже - я согласен.
> Перл выигрывает по критериям (2) и (3) (но читать код на перле нельзя), питон - (1) и (3) (но рекламы в мейнстриме ему явно не хватает).
Если на перле писать понятно, он и будет понятен. А хохмы с той же видимостью не дают питону достаточного пункта (1):) В общем, я бы тут акценты расставил совсем иначе.