Редактировать :
Смотрите мой полный ответ внизу этого вопроса.
tl; dr answer : Python имеетстатически вложенные области видимости. static может взаимодействовать с неявными объявлениями переменных, давая неочевидные результаты.
(Это может быть особенно удивительно из-за общей динамической природы языка).
Мне показалось, что я достаточно хорошо разбираюсь в правилах области видимости Python, но эта проблема полностью остановила меня, и мое google-fu подвело меня (не то, чтобы я удивился - посмотрите на заголовок вопроса;)
Я собираюсь начать с нескольких примеров, которые работают, как и ожидалось, но не стесняйтесь перейти к примеру 4 для сочной части.
Пример 1.
>>> x = 3
>>> class MyClass(object):
... x = x
...
>>> MyClass.x
3
Достаточно просто: во время определения класса мы можем получить доступ к переменным, определенным во внешней (в данном случае глобальной) области видимости.
Пример 2.
>>> def mymethod(self):
... return self.x
...
>>> x = 3
>>> class MyClass(object):
... x = x
... mymethod = mymethod
...
>>> MyClass().mymethod()
3
Опять же (игнорируя на данный момент почему кто-то может захотеть сделать это), здесь нет ничего неожиданного: мы можем получить доступ к функциям во внешней области.
Примечание: в качестве фредерикаг ниже, эта функция, кажется, не работает.Вместо этого см. Пример 5 (и далее).
Пример 3.
>>> def myfunc():
... x = 3
... class MyClass(object):
... x = x
... return MyClass
...
>>> myfunc().x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in myfunc
File "<stdin>", line 4, in MyClass
NameError: name 'x' is not defined
По сути, это то же самое, что и в примере 1: мы получаем доступ квнешняя область видимости из определения класса, только в этот раз эта область не является глобальной, благодаря myfunc()
.
Редактировать 5: Как указано @ user3022222ниже , я испортил этот пример в моей первоначальной публикации.Я считаю, что это терпит неудачу, потому что только функции (не другие блоки кода, как это определение класса) могут получить доступ к переменным в охватывающей области.Для блоков нефункционального кода доступны только локальные, глобальные и встроенные переменные.Более подробное объяснение доступно в этом вопросе
Еще одно:
Пример 4.
>>> def my_defining_func():
... def mymethod(self):
... return self.y
... class MyClass(object):
... mymethod = mymethod
... y = 3
... return MyClass
...
>>> my_defining_func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_defining_func
File "<stdin>", line 5, in MyClass
NameError: name 'mymethod' is not defined
Гм... простите?
Чем это отличается от примера 2?
Я совершенно сбит с толку.Пожалуйста, разберитесь со мной.Спасибо!
PS из-за того, что это не просто проблема с моим пониманием, я пробовал это на Python 2.5.2 и Python 2.6.2.К сожалению, это все, к чему у меня есть доступ в данный момент, но они оба демонстрируют одинаковое поведение.
Редактировать Согласно http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces: в любой момент во время выполнения,не менее трех вложенных областей, пространства имен которых доступны напрямую:
- самая внутренняя область, которая ищется первой, содержит локальные имена
- области всех вмещающих функций, поиск которых начинается сближайшая охватывающая область, содержит нелокальные, но также и неглобальные имена
- следующая за последней область содержит глобальные имена текущего модуля
- самая внешняя область (последняя найденная) - этопространство имен, содержащее встроенные имена
# 4.кажется контрпримером ко второму из них.
Edit 2
Пример 5.
>>> def fun1():
... x = 3
... def fun2():
... print x
... return fun2
...
>>> fun1()()
3
Редактировать 3
Как указал @ Frédéric, присваивание переменной с тем же именем, что и во внешней области видимости, «маскирует» внешнюю переменную, предотвращая назначение изфункционирует.
Так что эта модифицированная версия примера 4 работает:
def my_defining_func():
def mymethod_outer(self):
return self.y
class MyClass(object):
mymethod = mymethod_outer
y = 3
return MyClass
my_defining_func()
Однако это не так:
def my_defining_func():
def mymethod(self):
return self.y
class MyClass(object):
mymethod_temp = mymethod
mymethod = mymethod_temp
y = 3
return MyClass
my_defining_func()
Я до сих пор не до конца понимаю, почему этопроисходит маскирование: не должно ли происходить связывание имени при назначении?
В этом примере по крайней мере содержится некоторая подсказка (и более полезное сообщение об ошибке):
>>> def my_defining_func():
... x = 3
... def my_inner_func():
... x = x
... return x
... return my_inner_func
...
>>> my_defining_func()()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_inner_func
UnboundLocalError: local variable 'x' referenced before assignment
>>> my_defining_func()
<function my_inner_func at 0xb755e6f4>
Таким образом, кажется, чтолокальная переменная определяется при создании функции (что успешно), в результате чего локальное имя «резервируется» и, таким образом, маскируется имя внешней области при вызове функции.
Интересно.
Спасибо Фредерику за ответ (ы)!
Для справки: документы по питону :
Важно понимать, что области действия определяются текстуально:Глобальная область действия функции, определенной в модуле, - это пространство имен этого модуля, независимо от того, откуда и каким псевдонимом вызывается функция.С другой стороны, фактический поиск имен выполняется динамически, во время выполнения - однако определение языка развивается в сторону статического разрешения имен, во время «компиляции», поэтому не полагайтесь на динамическое разрешение имен!(Фактически, локальные переменные уже определены статически.)
Редактировать 4
Реальный ответ
Это, казалось бы, запутанное поведение вызвано статически вложенными областями Python, как определено в PEP 227 .На самом деле это не имеет ничего общего с PEP 3104 .
Начиная с PEP 227:
Правила разрешения имен типичны для языков со статической областью [...][кроме] переменные не объявлены.Если операция привязки имени происходит где-либо в функции, то это имя обрабатывается как локальное для функции, и все ссылки ссылаются на локальную привязку.Если ссылка возникает до того, как имя привязано, возникает ошибка NameError.
[...]
Пример из Tim Peters демонстрирует потенциальные ловушки вложенных областей действия при отсутствии объявлений:
i = 6
def f(x):
def g():
print i
# ...
# skip to the next page
# ...
for i in x: # ah, i *is* local to f, so this is what g sees
pass
g()
Вызов g () будет ссылаться на переменную i, связанную в f () циклом for.Если g () вызывается до выполнения цикла, возникает ошибка NameError.
Позволяет запустить две более простые версии примера Тима:
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... i = x
... g()
...
>>> f(3)
3
, когда g()
не делаетНе находя i
во внутренней области видимости, он динамически ищет снаружи, находя область i
в области f
, которая была связана с 3
посредством присваивания i = x
.
Но изменение порядка последних двух операторов в f
вызывает ошибку:
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... g()
... i = x # Note: I've swapped places
...
>>> f(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in f
File "<stdin>", line 3, in g
NameError: free variable 'i' referenced before assignment in enclosing scope
Помня, что в PEP 227 сказано: «Правила разрешения имен типичны для статически ограниченных языков», давайте посмотрим на) эквивалентное предложение версии C:
// nested.c
#include <stdio.h>
int i = 6;
void f(int x){
int i; // <--- implicit in the python code above
void g(){
printf("%d\n",i);
}
g();
i = x;
g();
}
int main(void){
f(3);
}
скомпилируйте и запустите:
$ gcc nested.c -o nested
$ ./nested
134520820
3
Так что, в то время как C с радостью будет использовать несвязанную переменную (используя все, что там было раньше: 134520820в этом случае) Python (к счастью) отказывается.
В качестве интересного примечания статически вложенные области видимости позволяют то, что Алекс Мартелли назвал"единственной наиболее важной оптимизацией, которую выполняет компилятор Pythones: локальные переменные функции не хранятся в dict, они находятся в узком векторе значений, и каждая локальная переменная доступа использует индекс в этом векторе, а не поиск имени. "