2013-11-27 2 views
48

Это сообщение немного длинное со многими примерами, но я надеюсь, что он поможет мне и другим людям лучше понять всю историю переменных и найти атрибут в Python 2.7.Как ссылки на переменные разрешены в Python

Я использую условие PEP 227 (http://www.python.org/dev/peps/pep-0227/) для кодовых блоков (такие, как модулей, определения класса, определения функций и т.д.) и привязок переменных (например, назначений, деклараций аргументов, класса и функций декларация, для петель и т.д.)

Я использую переменные термины для имен, которые могут быть вызваны без точки, и атрибуты для имен, которые должны быть квалифицированы с именем объекта (например, obj.x для атрибут x объекта obj).

Есть три области применения в Python для всех кодовых блоков, но функции:

  • Местного
  • Global
  • Builtin

Есть четыре блока в Python для функций только (в соответствии с PEP 227):

  • Местные
  • Ограждающие функции
  • Global
  • Builtin

Правило для переменной, чтобы связать его и найти его в блоке довольно просто:

  • обязывающих переменной к объекту в блоке делает эту переменную локальной для этого блока, если только переменная не объявлена ​​глобальной (в этом случая, переменный принадлежит к глобальной области)
  • ссылка на переменный ищутся с использованием правил LGB (локальная, глобальными, встроенным) для всех блоков, но функции
  • ссылки на переменный ищутся используя правило LEGB (локальное, , охватывающее, глобальное, встроенное) только для функций.

Сообщите мне об этом решении и дайте множество примеров, например, . Для каждого примера я дам свое понимание. Пожалуйста, исправьте меня, если я ошибаюсь. В последнем примере я не понимаю результат .

Пример 1:

x = "x in module" 
class A(): 
    print "A: " + x     #x in module 
    x = "x in class A" 
    print locals() 
    class B(): 
     print "B: " + x     #x in module 
     x = "x in class B" 
     print locals() 
     def f(self): 
      print "f: " + x    #x in module 
      self.x = "self.x in f" 
      print x, self.x 
      print locals() 

>>>A.B().f() 
A: x in module 
{'x': 'x in class A', '__module__': '__main__'} 
B: x in module 
{'x': 'x in class B', '__module__': '__main__'} 
f: x in module 
x in module self.x in f 
{'self': <__main__.B instance at 0x00000000026FC9C8>} 

Там нет вложенной возможности для классов (правило LGB) и функции в класс не может получить доступ к атрибутам класса без использования квалифицированного имени (self.x в этом примере). Это хорошо описано в PEP227.

Пример 2:

z = "z in module" 
def f(): 
    z = "z in f()" 
    class C(): 
     z = "z in C" 
     def g(self): 
      print z 
      print C.z 
    C().g() 
f() 
>>> 
z in f() 
z in C 

Здесь переменные в функциях ищутся с помощью правила LEGB, но если класса находится в пути, аргументы класса пропускается. И здесь это то, что объясняет PEP 227.

Пример 3:

var = 0 
def func(): 
    print var 
    var = 1 
>>> func() 

Traceback (most recent call last): 
    File "<pyshell#102>", line 1, in <module> 
func() 
    File "C:/Users/aa/Desktop/test2.py", line 25, in func 
print var 
UnboundLocalError: local variable 'var' referenced before assignment 

Мы ожидаем, что с динамическим языком, таких как питон, что все решена динамически. Но это не относится к функциям. Локальные переменные определяются во время компиляции. PEP 227 и http://docs.python.org/2.7/reference/executionmodel.html описывают эту поведение на этот путь

«Если имя привязки операция происходит в любом месте в пределах блока кода, все использования имени внутри блока трактуются как ссылки на текущего блока.»

Пример 4:

x = "x in module" 
class A(): 
    print "A: " + x 
    x = "x in A" 
    print "A: " + x 
    print locals() 
    del x 
    print locals() 
    print "A: " + x 
>>> 
A: x in module 
A: x in A 
{'x': 'x in A', '__module__': '__main__'} 
{'__module__': '__main__'} 
A: x in module 

Но здесь мы видим, что это утверждение в PEP227 «Если имя связывание операции происходит в любом месте в пределах блока кода, все виды использование имени в пределах блока, рассматривается в качестве ссылок к текущему блоку. " неверно, если блок кода является классом. Более того, для классов кажется , что привязка локального имени не выполняется во время компиляции, но во время выполнения с использованием пространства имен классов. В этом отношении PEP227 и модель исполнения в документе Python вводит в заблуждение, а для некоторые части ошибочны.

Пример 5:

x = 'x in module' 
def f2(): 
    x = 'x in f2' 
    def myfunc(): 
     x = 'x in myfunc' 
     class MyClass(object): 
      x = x 
      print x 
     return MyClass 
    myfunc() 
f2() 
>>> 
x in module 

мое понимание этого кода заключается в следующем. Команда x = x сначала ищет объект правой рукой x выражения ссылается на. В этом случае объект просматривается локально в классе, затем , следуя правилу LGB, он просматривается в глобальной области, которая равна строке «x в модуле». Затем локальный атрибут x для MyClass составляет , созданный в словаре классов, и указывает на строковый объект.

Пример 6:

Теперь вот пример, который я не могу объяснить. Это очень близко к примеру 5, я просто изменяю локальный атрибут MyClass от x до y.

x = 'x in module' 
def f2(): 
    x = 'x in f2' 
    def myfunc(): 
     x = 'x in myfunc' 
     class MyClass(object): 
      y = x 
      print y 
     return MyClass 
    myfunc() 
f2() 
>>> 
x in myfunc 

Почему в этом случае х ссылка в MyClass ищется в самой внутренней функции ?

+3

Очень сложно сказать, что должно происходить в последних нескольких примерах с отступом так, как оно есть - не могли бы вы исправить это? (Помните, что 4-сегментный отступ создает блок кода - каждое пространство после этого показано как пробел в образце кода). –

+0

Это похоже на очень интересный вопрос, но, пожалуйста, исправьте углубление. – thefourtheye

+0

@SeanVieira благодарит за замечание. У меня было много вкладок вместо пробелов. Теперь это исправлено. – user3022222

ответ

18

В двух словах разница между примером 5 и примером 6 заключается в том, что в примере 5 переменная x также назначается в той же области действия, но не в примере 6. Это вызывает разницу, которая может быть понята по историческим причинам ,

Это поднимает UnboundLocalError:

x = "foo" 
def f(): 
    print x 
    x = 5 
f() 

вместо печати "Foo". Это имеет смысл, даже если сначала кажется странным: функция f() определяет переменную x локально, даже если она находится после печати, и поэтому любая ссылка на x в той же функции должна быть на эту локальную переменную , По крайней мере, имеет смысл то, что он избегает странных сюрпризов, если вы по ошибке повторно использовали имя глобальной переменной локально и пытаетесь использовать как глобальную, так и локальную переменную. Это хорошая идея, потому что это означает, что мы можем статически знать, просто взглянув на переменную , которая означает переменную. Например, мы знаем, что print x относится к локальной переменной (и, следовательно, может вызвать UnboundLocalError) здесь:

x = "foo" 
def f(): 
    if some_condition: 
     x = 42 
    print x 
f() 

Теперь это правило не работает для прицелов уровня класса: есть, мы хотим, чтобы такие выражения, как x = x к работать, захватывая глобальную переменную x в область уровня класса. Это означает, что прицелы класса уровня не следовать основному правилу выше: мы не можем знать, если x в этой области относится к некоторой внешней переменной или локально определенные x --- например:

class X: 
    x = x  # we want to read the global x and assign it locally 
    bar = x # but here we want to read the local x of the previous line 

class Y: 
    if some_condition: 
     x = 42 
    print x  # may refer to either the local x, or some global x 

class Z: 
    for i in range(2): 
     print x # prints the global x the 1st time, and 42 the 2nd time 
     x = 42 

Итак, в классах используется другое правило: там, где он обычно поднимает UnboundLocalError --- и только в этом случае - вместо этого он ищет в глобалях модуля. Вот и все: он не следует цепочке вложенных областей.

Почему нет? Я действительно сомневаюсь, что есть лучшее объяснение, что «по историческим причинам». В более технических терминах он мог бы считать, что переменная x локально определена в области видимости класса (поскольку ей присвоено) , а должно быть передано из родительской области как лексически вложенная переменная (поскольку она считывается). Его можно было бы реализовать, используя другой байт-код, чем LOAD_NAME, который просматривает локальную область и возвращается к использованию ссылки вложенной области видимости, если не найден.

EDIT: благодарит wilberforce за ссылку http://bugs.python.org/issue532860. Возможно, у нас будет возможность активировать дискуссию с предлагаемым новым байт-кодом, если мы почувствуем, что он должен быть исправлен в конце (отчет об ошибке содержит информацию об убийстве поддержки x = x, но был закрыт из-за боязни сломать слишком много существующего кода, вместо этого я Предполагая, что здесь будет делать работу x = x в большем числе случаев). Или я могу отсутствовать еще один тонкий момент ...

EDIT2: кажется, что CPython сделал точно, что в текущем 3.4 стволу: http://bugs.python.org/issue17853 ... или нет? Они ввели байт-код по несколько иной причине и не используют его систематически ...

+0

Ключевым отличием является то, что операторы и выражения в теле функции вычисляются, когда функция * называется *; операторы и выражения в тексте оператора 'class' выполняются как часть инструкции' class' (т. е. ранее). В функции 'some_condition' может быть или не быть истинным, поэтому' x' должен обладать одинаковой областью каждый раз. Оператор 'class' выполняется один раз, поэтому к моменту достижения' print x' мы знаем, является ли 'x' локальным или глобальным к моменту его достижения. – chepner

+0

Моя точка зрения заключается в том, что в функциях мы знаем, если 'x' является локальным или глобальным к моменту запуска компилятора байт-кода, прежде чем что-либо оценивать. Более сложные примеры можно сделать с помощью цикла 'for' в' class', где одно и то же использование одной и той же переменной 'x' не всегда разрешено для одной и той же области. –

6

Короче говоря, это угловой случай области обзора Python, который немного противоречив, но должен быть сохранен для обратной совместимости (и потому, что не ясно, какой должен быть правильный ответ). Вы можете увидеть много original discussion об этом в списке рассылки Python при реализации PEP 227, а некоторые в bug, для которых это поведение является исправлением.

Мы можем решить, почему существует разница с использованием модуля dis, который позволяет нам смотреть внутри объектов кода, чтобы увидеть байт-код, скомпилированный фрагмент кода. Я на Python 2.6, поэтому детали этого могут быть немного разными - но я вижу такое же поведение, поэтому думаю, что он, вероятно, достаточно близко к 2.7.

Код, инициализирующий каждый вложенный MyClass, находится в объекте кода, доступ к которому можно получить через атрибуты функций верхнего уровня. (Я переименовывать функции из примера 5 и примера 6, f1 и f2 соответственно.)

Объект кода имеет co_consts кортеж, который содержит объект myfunc кода, который, в свою очередь, имеет код, который выполняется, когда MyClass получает создано:

In [20]: f1.func_code.co_consts 
Out[20]: (None, 
'x in f2', 
<code object myfunc at 0x1773e40, file "<ipython-input-3-6d9550a9ea41>", line 4>) 
In [21]: myfunc1_code = f1.func_code.co_consts[2] 
In [22]: MyClass1_code = myfunc1_code.co_consts[3] 
In [23]: myfunc2_code = f2.func_code.co_consts[2] 
In [24]: MyClass2_code = myfunc2_code.co_consts[3] 

Тогда вы можете увидеть разницу между ними в байткод, используя dis.dis:

In [25]: from dis import dis 
In [26]: dis(MyClass1_code) 
    6   0 LOAD_NAME    0 (__name__) 
       3 STORE_NAME    1 (__module__) 

    7   6 LOAD_NAME    2 (x) 
       9 STORE_NAME    2 (x) 

    8   12 LOAD_NAME    2 (x) 
      15 PRINT_ITEM   
      16 PRINT_NEWLINE  
      17 LOAD_LOCALS   
      18 RETURN_VALUE   

In [27]: dis(MyClass2_code) 
    6   0 LOAD_NAME    0 (__name__) 
       3 STORE_NAME    1 (__module__) 

    7   6 LOAD_DEREF    0 (x) 
       9 STORE_NAME    2 (y) 

    8   12 LOAD_NAME    2 (y) 
      15 PRINT_ITEM   
      16 PRINT_NEWLINE  
      17 LOAD_LOCALS   
      18 RETURN_VALUE   

Так только differe nce - то, что в MyClass1, x загружается с использованием LOAD_NAME op, тогда как в MyClass2 он загружается с использованием LOAD_DEREF. LOAD_DEREF ищет имя в закрывающей области, поэтому он получает «x в myfunc». LOAD_NAME не следует за вложенными областями - поскольку он не может видеть имена x, связанные в myfunc или f1, он получает привязку на уровне модуля.

Тогда возникает вопрос: почему код двух версий MyClass скомпилирован с двумя разными кодами операций? В f1 привязка затеняет x в области класса, а в f2 - это привязка нового имени. Если MyClass прицелов были вложены функции вместо классов, y = x линия в f2 будет составлена ​​таким же, но x = x в f1 будет LOAD_FAST - это потому, что компилятор будет знать, что x связывается в функции, поэтому она должна используйте LOAD_FAST для получения локальной переменной. Это не сработало бы с UnboundLocalError, когда он был вызван.

In [28]: x = 'x in module' 
def f3(): 
    x = 'x in f2' 
    def myfunc(): 
     x = 'x in myfunc' 
     def MyFunc(): 
      x = x 
      print x 
     return MyFunc() 
    myfunc() 
f3() 
--------------------------------------------------------------------------- 
Traceback (most recent call last) 
<ipython-input-29-9f04105d64cc> in <module>() 
     9   return MyFunc() 
    10  myfunc() 
---> 11 f3() 

<ipython-input-29-9f04105d64cc> in f3() 
     8    print x 
     9   return MyFunc() 
---> 10  myfunc() 
    11 f3() 

<ipython-input-29-9f04105d64cc> in myfunc() 
     7    x = x 
     8    print x 
----> 9   return MyFunc() 
    10  myfunc() 
    11 f3() 

<ipython-input-29-9f04105d64cc> in MyFunc() 
     5   x = 'x in myfunc' 
     6   def MyFunc(): 
----> 7    x = x 
     8    print x 
     9   return MyFunc() 

UnboundLocalError: local variable 'x' referenced before assignment 

Это не удается, потому что функция MyFunc затем использует LOAD_FAST:

In [31]: myfunc_code = f3.func_code.co_consts[2] 
MyFunc_code = myfunc_code.co_consts[2] 
In [33]: dis(MyFunc_code) 
    7   0 LOAD_FAST    0 (x) 
       3 STORE_FAST    0 (x) 

    8   6 LOAD_FAST    0 (x) 
       9 PRINT_ITEM   
      10 PRINT_NEWLINE  
      11 LOAD_CONST    0 (None) 
      14 RETURN_VALUE   

(Кстати, это не является большой неожиданностью, что должна быть разница в том, как обзорное взаимодействует с кодом в теле классы и код в функции. Вы можете это сказать, потому что привязки на уровне класса недоступны в методах. Области методов не вложены внутри области видимости класса так же, как вложенные функции. Вы должны явно связаться с ними через класс или с помощью self. (который вернется к классу, если нет lso связывание на уровне экземпляра).

+2

Человек, неудача на этом - я положил всю эту работу в ответ, а кто еще прыгает? Один из ведущих разработчиков в самой быстрой реализации Python, которому пришлось реплицировать множество приконов CPython, подобных этой для совместимости. И Martijn Pieters, у которого есть 182k, потому что он, по-видимому, всегда дает фантастические ответы, как тот, что он здесь сделал. Ну что ж! – babbageclunk

21

В идеальном мире вы были бы правы, и некоторые из несоответствий, которые вы нашли, были бы неправильными. Тем не менее, CPython оптимизировал некоторые сценарии, особенно функции locals. Эти оптимизации, вместе с тем, как взаимодействует цикл компилятора и оценки и исторический прецедент, приводят к путанице.

Python преобразует код в байт-коды, а затем интерпретируется с помощью цикла интерпретатора. «Обычный» код операции для доступа к имени - LOAD_NAME, который ищет имя переменной, как в словаре. LOAD_NAME сначала ищет имя как локальное, и если это не удается, он ищет глобальный. LOAD_NAME throws a NameError исключение, когда имя не найдено.

Для вложенных областей поиск имен за пределами текущей области применения осуществляется с помощью закрытий; если имя не назначено, но доступно во вложенной (не глобальной) области, то такие значения обрабатываются как закрытие. Это необходимо, поскольку родительская область может содержать разные значения для заданного имени в разное время; два вызова родительской функции могут приводить к различным значениям закрытия. Таким образом, у Python есть LOAD_CLOSURE, MAKE_CLOSURE и LOAD_DEREF опкод для этой ситуации; первые два кода операций используются при загрузке и создании замыкания для вложенной области, а LOAD_DEREF будет загружать значение закрытого значения, когда ему нужна вложенная область.

В настоящее время LOAD_NAME относительно медленный; он будет консультироваться с двумя словарями, что означает, что он должен сначала хэшировать ключ и выполнить несколько тестов на равенство (если имя не было интернировано). Если имя не является локальным, то оно должно сделать это снова для глобального. Для функций, которые потенциально можно назвать десятками тысяч раз, это может стать утомительным. Таким образом, функции locals имеют специальные коды операций. Загрузка локального имени осуществляется с помощью LOAD_FAST, который ищет локальные переменные по индексу в специальном массиве локальных имен. Это намного быстрее, но для этого требуется, чтобы компилятор сначала обнаружил, является ли имя локальным, а не глобальным. Чтобы все еще иметь возможность искать глобальные имена, используется другой код операции LOAD_GLOBAL. Компилятор явно оптимизирует для этого случая создание специальных кодов операций. LOAD_FAST будет вызывать исключение UnboundLocalError, если еще нет значения для имени.

Корпусы определения класса, с другой стороны, хотя они обрабатываются как функция, не получают этот шаг оптимизации. Определения классов не должны называться так часто; большинство модулей создают классы один раз, при импорте. Области классов не учитываются при вложенности, поэтому правила проще. В результате тела определения класса не действуют как функции, когда вы немного начинаете смешивать области.

Таким образом, для нефункциональных областей LOAD_NAME и LOAD_DEREF используются для локальных и глобальных соединений, а также для замыканий соответственно. Для функций вместо этого используются LOAD_FAST, LOAD_GLOBAL и LOAD_DEREF.

Обратите внимание, что тела классов выполняются, как только Python выполняет линию class! Таким образом, в примере 1 class B внутри class A выполняется, как только выполняется class A, то есть при импорте модуля. В примере 2 C не выполняется до тех пор, пока не будет выдан f(), а не раньше.

Позволяет пройти через ваши примеры:

  1. Вы вложен класс A.B в классе A. Тело класса не образует вложенных областей, поэтому, хотя тело класса A.B выполняется, когда выполняется класс A, компилятор будет использовать LOAD_NAME для поиска x. A.B().f() - это функция (связанная с экземпляром B() как метод), поэтому он использует LOAD_GLOBAL для загрузки x. Мы будем игнорировать доступ к атрибутам здесь, это очень четко определенный шаблон имени.

  2. f().C.z Здесь находится область видимости класса, так что функция f().C().g() пропустит C рамки и посмотреть на f() сфере вместо этого, используя LOAD_DEREF.

  3. Здесь var был определен локальным компилятором, поскольку вы назначаете его в пределах области действия. Функции оптимизированы, поэтому LOAD_FAST используется для поиска локального объекта и генерируется исключение.

  4. Теперь все становится немного странным. class A выполняется в классе scope, поэтому используется LOAD_NAME. A.x был удален из локального словаря для области, поэтому второй доступ к x приводит к обнаружению глобального x; LOAD_NAME искал локальный первый и не нашел его там, возвращаясь к глобальному поиску.

    Да, это не соответствует документации. Python-the-language и CPython - реализация здесь немного конфликтуют. Вы, однако, выдвигаете границы того, что возможно и практично на динамическом языке; если x должен был быть локальным в LOAD_NAME, это будет возможно, но потребует драгоценного времени выполнения для углового футляра, который большинство разработчиков никогда не столкнется.

  5. Теперь вы сбиваете с толку компилятор. Вы использовали x = x в области видимости класса, и, таким образом, вы устанавливаете локальный с именем за пределами области действия. Компилятор считает, что x является локальным здесь (вы его назначаете), поэтому он никогда не считает, что это также может быть имя области действия. Компилятор использует LOAD_NAME для всех ссылок на x в этой области, потому что это не оптимизированное тело функции.

    При выполнении определения класса, x = x первый требует, чтобы вы посмотреть x, поэтому он использует LOAD_NAME сделать это. Нет x определено, LOAD_NAME не найдено, поэтому глобальноx не найден. Результирующее значение сохраняется как локальное, которое имеет, которое будет называться x. print x снова использует LOAD_NAME, а теперь находит новое локальное значение x.

  6. Здесь вы не смутили компилятор. Вы создаете локальный y, x не является локальным, поэтому компилятор распознает его как имя области действия от родительской функции f2().myfunc(). x проверяется с помощью LOAD_DEREF с закрытием и хранится в y.

Вы можете видеть путаницу между 5 и 6 в качестве ошибки, хотя и не заслуживающей фиксации, по моему мнению. Он был, конечно, подан как таковой, см. issue 532860 в Python отладчик ошибок, он существует уже более 10 лет.

компилятор может проверить на контекстное имя x даже когда x является также местным, для этого первого задания в примере 5. Или LOAD_NAME может проверить, если имя предназначается, чтобы быть местными, на самом деле, и бросаться UnboundLocalError, если местное не было найдено, за счет большей производительности. Если бы это было в области функций, то LOAD_FAST использовали бы, например, 5, и UnboundLocalError будет немедленно выброшен.

Однако, как показывает ссылка, по историческим причинам поведение сохраняется.Вероятно, сегодня есть код, который сломался, если бы эта ошибка была исправлена.

+0

Спасибо за объяснение. Тем не менее, я до сих пор не понимаю, почему в примере 6, x считается скопированным именем. x относится к классу, я понимаю, что в этом случае его следует искать локально, а затем глобально. Кроме того, предполагается, что PEP 227 предназначен только для функций, а не для классов, и в примере 1 мы видим, что классы не используют закрытие области поиска. Что особенное в примере 6, которое объясняет, что указанная переменная в классе рассматривается как скопированная переменная? – user3022222

+0

Нет, PEP 227 также относится к определениям классов; единственным исключением является то, что определения классов не образуют область для вложенных кодовых блоков. Поэтому в примере 1 определение класса 'A' ​​не считается родительской областью для имен в классе' B', поэтому 'x' в' B' не использует 'Ax', а смотрит на глобальный' x' , В примере 6 родительская функция ** делает ** форму области, поэтому «x» просматривается из этой области. Единственным исключением здесь является пример 5, где 'x' действительно является локальным в' B', а для этого используется код операции LOAD_NAME'. Тот факт, что 'x = x' работает * вообще *, возможно, является ошибкой. –

+0

Хорошо, я пропустил эту тонкость и ошибочно полагал, что если классы не образуют область для вложенного блока кода, они полностью избегают разрешения именных областей. Это окончательно ни прямолинейно, ни интуитивно, ни хорошо документировано. Эти проблемы заслуживают примеров, и я надеюсь, что мой пост поможет в этом отношении. Спасибо за все разъяснения. – user3022222

Смежные вопросы