/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-27 09:25 am (UTC)Чем теперь заменить?
no subject
Date: 2006-08-27 09:26 am (UTC)no subject
Date: 2006-08-27 09:30 am (UTC)no subject
Date: 2006-08-27 09:33 am (UTC)типа
тада работает.
no subject
Date: 2006-08-27 09:46 am (UTC)no subject
Date: 2006-08-27 09:47 am (UTC)no subject
Date: 2006-08-27 09:49 am (UTC)no subject
Date: 2006-08-27 09:53 am (UTC)no subject
Date: 2006-08-27 12:23 pm (UTC)no subject
Date: 2006-08-27 12:26 pm (UTC)no subject
Date: 2006-08-27 02:02 pm (UTC)no subject
Date: 2006-08-27 04:36 pm (UTC)В обоих примерах с global оно используется неправильно. (При этом в качестве извинения приводится "лаконичность документации".)
Последний пример с import тоже не имеет никакого смысла. "Import ... defines a name or names in the local namespace ... From<>import ... binds the name to the object thus found." Наличие from A import g1 в модуле main вводит имя g1 в каталоге модуля main и связывает его с объектом из модуля A (в данном случае None).
Короче, надо читать документацию внимательно, перед тем как поднимать панику, что Питон вымрет.
no subject
Date: 2006-08-27 04:50 pm (UTC)Которые примеры "оба"? Первый случай? Это я недочистил перед постингом. Там были ещё недочистки с расхождением названия, можно было их заметить и понять, что к чему:)))
> Последний пример с import тоже не имеет никакого смысла. "Import ... defines a name or names in the local namespace ... From<>import ... binds the name to the object thus found." Наличие from A import g1 в модуле main вводит имя g1 в каталоге модуля main и связывает его с объектом из модуля A (в данном случае None).
Правильно, введено в. И если ты сделаешь в main() чтение значения g1, то у тебя почему-то:) прочтётся A.g1. А вот присвоение - пойдёт отдельно.
> Короче, надо читать документацию внимательно, перед тем как поднимать панику, что Питон вымрет.
Во-первых, я паники не поднимал и не говорил что он вымрет (кто из нас читает-то невнимательно?)
Во-вторых, речь о том что логика крайне извращённая по сравнению и с привычной, и со здравым смыслом (а привычная как раз более осмысленная). Если переменная видна, то она должна быть видна независимо от операций которые с ней производятся. Если не видна - тоже. Есть конечно случаи специализированных прокси, но они на то и явно отдельные прокси. А тут - ты можешь делать с переменной что угодно, кроме присваивания - можешь даже __dict__ ей заменить после чего она превратится во что-то полностью другое - но не можешь ей присвоить новое значение, даже если оно совпадает с предыдущим или есть минимальная его правка. Ну не маразм?
no subject
Date: 2006-08-27 06:01 pm (UTC)> если ты сделаешь в main() чтение значения g1, то у тебя почему-то:) прочтётся A.g1. А вот присвоение - пойдёт отдельно.
Еще раз, постепенно.
Мне кажется, ты понимаешь import как способ изменить видимость переменных. Это не так.
"from A import g1" вводит имя g1 в модуле main. С этим именем связывается объект, который был связан с именем g1 в модуле A. Объект, а не имя. Мы не начинаем видеть переменную A.g1 как g1, мы получаем новую переменную g1, которая в начальный момент имеет то же значение.
После этого, когда ты делаешь присваивание g1 в main, ты изменяешь связывание этого имени в main. На каталог A это не оказывает никакого влияния. Что твой пример и иллюстирует.
Для полной ясности, попробуй вот так:
#!/usr/bin/env python import A from A import g1 print id(g1), id(A.g1) # both variables are bound with the same object g1 = {} print id(g1), id(A.g1) # variable in main is rebound, object identities are different A.g1 = {} del(g1) from A import g1 print id(g1), id(A.g1) # variable in A is rebound, g1 is reimported, now # they are the same againЛогика вполне нормальная, просто не надо накладывать свои некорректные интерпретации на спецификацию языка.
(Я бы не завелся, если бы единственным твоим тезисом было "у Питона неадекватная документация", что есть правда.)
no subject
Date: 2006-08-27 06:20 pm (UTC)> "from A import g1" вводит имя g1 в модуле main. С этим именем связывается объект, который был связан с именем g1 в модуле A. Объект, а не имя. Мы не начинаем видеть переменную A.g1 как g1, мы получаем новую переменную g1, которая в начальный момент имеет то же значение.
Никакого "начального момента" нет, это всё время одна и та же переменная, введённая в глобальные данного модуля. Что чётко и сказано в документации.
> После этого, когда ты делаешь присваивание g1 в main, ты изменяешь связывание этого имени в main. На каталог A это не оказывает никакого влияния. Что твой пример и иллюстирует.
А вот когда я делаю присваивание g1, происходит присваивание другой g1 - локальной для данного блока. Цитата из документации была в постинге. Это можно проверить и практически:
Запускаем:
> Логика вполне нормальная,
Нет. ™
> просто не надо накладывать свои некорректные интерпретации на спецификацию языка.
Некорректных интерпретаций нет. Я начал писать тот постинг когда разобрался что же происходит и где оно в документации.
> (Я бы не завелся, если бы единственным твоим тезисом было "у Питона неадекватная документация", что есть правда.)
В каком смысле неадекватная? По-моему как раз документация адекватна реализации. И вообще она очень неплохая - после того как разберёшься с парой общих принципов построения. А вот реализация в данном месте резко неадекватна здравому смыслу.
no subject
Date: 2006-08-27 06:38 pm (UTC)Совершенно неверно. Опять же, читай описание import, del, и изучай мой пример. Ты его прочел и запустил, ведь правда? И разобрался, как он работает, да?
no subject
Date: 2006-08-27 06:58 pm (UTC)1) 'from A import g1' обеспечивает что _одна и та же_ переменная A.g1 становится доступна в обоих модулях? А не возникает какая-то другая переменная?
2) присвоение в блоке где не сказано ни global ни import ни чего-то ещё подобного создаёт новую переменную с тем же именем, видимую время жизни блока?
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 04:58 am (UTC)no subject
Date: 2006-08-28 05:22 am (UTC)Убил.
> Поэтому вопрос к тебе: в чем собственно состоит "клиническая дурость" видимости имен в Питоне (если прочитать документацию)?
Давай я тебе отвечу вопросом на вопрос: чем такая схема принципиально лучше схемы в стиле, например, перла с его my, local, use vars и теми ограничениями которые накладываются при -Mstrict?
no subject
Date: 2006-08-28 05:27 am (UTC)А дальше - вопрос тёмный.
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 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 должен войти в школьную программу обучения, причем не так как его преподают в КПИ'ях.
no subject
Date: 2006-08-28 10:21 am (UTC)global_f = f
def jump():
f = ["jump"]
upvar_f = f
def kick():
f = ["kick"]
upvar_f.extend(f)
kick()
global_f.extend(f)
jump()
print f
не думаю что лучше было бы что-то вроде ../f = f + ../../f * /f
название - самая сложная вещь, но нужная.. :)
no subject
Date: 2006-08-28 10:22 am (UTC)f=["stand"] global_f = f def jump(): f = ["jump"] upvar_f = f def kick(): f = ["kick"] upvar_f.extend(f) kick() global_f.extend(f) jump() print fno 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 02:58 pm (UTC)no subject
Date: 2006-08-28 03:14 pm (UTC)local - да, нету.
no subject
Date: 2006-08-28 03:15 pm (UTC)Когда решалось что использовать - erlang, питон или ещё что-то - существенным козырем в пользу erlang'а была именно его автоматизированная и удобная схема управления перезагрузкой модулей.
no subject
Date: 2006-08-28 03:19 pm (UTC)no subject
Date: 2006-08-28 03:21 pm (UTC)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):) В общем, я бы тут акценты расставил совсем иначе.