Edit
Посмотрите мой полный ответ внизу этого вопроса.
tl; dr answer: Python имеет статически вложенные области. статическийаспект может взаимодействовать с неявными объявлениями переменных, что дает неочевидные результаты.
(Это может быть особенно удивительно из-за языка, обычно динамического характера).
Я думал, что у меня неплохая ручка в правилах определения 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?
Я полностью одурачен. Пожалуйста, разобраться со мной. Спасибо!
P.S. на случай, что это не просто проблема с моим пониманием, я пробовал это на Python 2.5.2 и Python 2.6.2. К сожалению, это все, к чему у меня есть доступ в настоящий момент, но они оба демонстрируют одинаковое поведение.
Edit Согласно http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces: в любое время во время выполнения есть как минимум три вложенных области, пространства имён которых доступны напрямую:
- самый внутренний масштаб, который поиск сначала, содержит локальный имена
- области применения любых функции, которые выполняются начиная с ближайшего область, содержит нелокальные, но также неглобальные имена
- следующая последняя область содержит текущие глобальные имена модулей
- внешний вид (последний поиск) это пространство имен, содержащее встроенный имена
# 4. представляется противоположным примером для второго из них.
Изменить 2
Пример 5.
>>> def fun1():
... x = 3
... def fun2():
... print x
... return fun2
...
>>> fun1()()
3
Изменить 3
Как заметил Фредерик, назначение переменной с тем же именем, что и во внешней области видимости, "маскирует" внешнюю переменную, предотвращая ее выполнение.
Итак, эта модифицированная версия примера 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>
Таким образом, кажется, что локальная переменная определена при создании функции (которая преуспевает), в результате локальное имя "зарезервировано" и, таким образом, маскирует имя внешней области при вызове функции.
Интересно.
Спасибо Фредерику за ответ (ы)!
Для справки: документы python:
Важно понимать, что области определяются текстовым: глобальный область действия функции, определенной в модуль - это пространство имен модулей, no вопрос от того, где или по тому, что вызывается функция. С другой стороны, выполняется фактический поиск имен динамически, во время выполнения - однако, определение языка развивается в направлении статического разрешения имен, при "компилировать" время, поэтому не полагайтесь на динамическое разрешение имен! (По факту, локальные переменные уже определены статически.)
Изменить 4
Реальный ответ
Это, казалось бы, запутанное поведение вызвано Python статически вложенными областями, как определено в PEP 227. Это фактически не имеет отношения к PEP 3104.
От PEP 227:
Правила разрешения имен типичны для языков с фиксированным охватом [...] [кроме] переменные не объявлены. Если происходит операция привязки имени в любом месте функции, то это имя рассматривается как локально для функции и все ссылки относятся к местным связывание. Если ссылка встречается раньше имя привязано, NameError поднят.
[...]
Пример от Тима Петерса демонстрирует потенциальные подводные камни вложенные области в отсутствие объявлений:
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(), для петля. Если 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 (к счастью) отказывается.
Как интересная заметка, статически вложенные области позволяют тому, что Алекс Мартелли назвал "единственной самой важной оптимизацией, выполняемой компилятором Python: локальные переменные функции не хранятся в dict, они находятся в плотном векторе значений, и каждый локальный доступ к переменной использует индекс в этом векторе, а не поиск имени."