Создание класса внутри функции и доступ к функции, определенной в области действия содержащейся функции

Редактировать :

См. Мой полный ответ внизу этого вопроса.

tl; dr answer : Python имеет статически вложенные области видимости. Статическая мы можем получить доступ к функциям во внешней области видимости.

Примечание : как Фредерик указал ниже, эта функция, похоже, не работает. Вместо этого см. Пример 5 (и далее).

Пример 3.

>>> def myfunc():
...     x = 3
...     class MyClass(object):
...         x = x
...     return MyClass
... 
>>> myfunc().x
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 3, in myfunc
  File "", 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 "", line 1, in 
  File "", line 4, in my_defining_func
  File "", 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. кажется контрпримером ко второму из них.

Редактировать 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 "", line 1, in 
  File "", line 4, in my_inner_func
UnboundLocalError: local variable 'x' referenced before assignment
>>> my_defining_func()

Таким образом, похоже, что локальная переменная определена в функции создание (которое завершается успешно), в результате чего локальное имя становится «зарезервированным» и таким образом маскирует имя внешней области при вызове функции.

Интересно. определены текстуально: глобальные объем функции, определенной в модуль - это пространство имен этого модуля, нет независимо от того, откуда и под каким псевдонимом функция вызывается. С другой стороны, фактический поиск имен выполняется динамически, во время выполнения - однако, определение языка развивается в сторону статического разрешения имен, при Время «компиляции», поэтому не полагайтесь на динамическое разрешение имен! (По факту, локальные переменные уже определены статически.)

Редактировать 4

Реальный ответ

Это, казалось бы, сбивающее с толку поведение вызвано статически вложенными областями действия Python, как определено в PEP 227 . На самом деле это не имеет ничего общего с PEP 3104 .

Из PEP 227:

Правила разрешения имен являются типичными для языков со статической областью видимости [...] [кроме] переменные не объявляются. Если происходит операция привязки имени в любом месте функции, тогда это имя рассматривается как локальный для функции и все ссылки относятся к местным привязка. Если ссылка встречается до имя связано, NameError поднял.

[...]

Пример Тима Петерса демонстрирует потенциальные ловушки вложенные области при отсутствии объявлений:

 i = 6
def f (x):
 def g ():
 распечатать я
 # ...
 # перейти на следующую страницу
 # ...
 for i in x: # ah, i * является * локальным для f, так что это то, что видит g
 проходить
 г()

Вызов g () будет ссылаться на переменную i, связанную в f () с помощью for петля. Если g () вызывается до выполнения цикла, ошибка NameError будет статически вложенные области видимости обеспечивают то, что Алекс Мартелли назвал «единственной наиболее важной оптимизацией, которую делает компилятор Python: локальные переменные функции не хранятся в dict, они находятся в узком векторе значений, и каждая локальная доступ к переменной использует индекс в этом векторе, а не поиск по имени. "

62
задан Community 23 May 2017 в 12:26
поделиться