Несвязанные локальные переменные в определении класса ищутся в глобальном пространстве имен - что это значит? - PullRequest
1 голос
/ 13 июня 2019

Последний абзац https://docs.python.org/3/reference/executionmodel.html#resolution-of-names говорит

Блоки определения класса и аргументы exec () и eval () являются особыми в контексте разрешения имен. Определение класса - это исполняемый оператор, который может использовать и определять имена. Эти ссылки следуют обычным правилам разрешения имен, за исключением того, что несвязанные локальные переменные ищутся в глобальном пространстве имен.

Что означает последнее предложение цитируемого текста? Сначала я сделал из этого вывод, что следующий код напечатает 1

a = 1

def foo():
    a = 2
    def bar():
        class Bar:
            b = a
        print(Bar.b)
    bar()

foo()

но я ошибся - модуль, состоящий из приведенного выше кода, при запуске печатает 2, то есть имя a в определении класса, даже если он не связан в блоке определения класса и не связан в Локальный блок, расположенный вне его, не ищется в глобальном пространстве имен, вопреки тому, что говорят документы.

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

a = 1

def foo():
    a = 2
    def bar():
        class Bar:
            del a
        print(Bar.b)
    bar()

foo()

но оператор del поднимает NameError: name 'a' is not defined.

Итак, я не понимаю, что означает это предложение?

Ответы [ 3 ]

6 голосов
/ 13 июня 2019

По документам ,

если имя связано в блоке, оно является локальной переменной этого блока, если только оно не объявлено как нелокальное или глобальное.

В вашем первом кодовом блоке a не привязан ни к чему в вашем определении class Bar, поэтому он не является локальной переменной этого блока.

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

a = 1
def foo():
    a = 2
    class Bar:
        b = a
        a = 3
    print(Bar.b)
foo()

Результат:

1

Это демонстрирует принцип «поиск несвязанных локальных переменных в глобальном пространстве имен» - b = a использует значение глобального a, а не значение a local для foo.


Во втором примере a считается локальным по отношению к блоку class Bar, поскольку "цель, встречающаяся в операторе del, также считается связанной" с целью определения области действия имени. Но «поиск несвязанных локальных переменных в глобальном пространстве имен» не имеет значения, так как del не нужно искать значение имени для его отмены.

Для большей достоверности мы можем экспериментально подтвердить, что оператор del сообщает интерпретатору, что имя следует считать локальным.

a = 1
def foo():
    a = 2
    class Bar:
        print(a)
        del a
foo()

Результат:

1
Traceback (most recent call last):
  File "C:\Users\Kevin\Desktop\test.py", line 7, in <module>
    foo()
  File "C:\Users\Kevin\Desktop\test.py", line 4, in foo
    class Bar:
  File "C:\Users\Kevin\Desktop\test.py", line 6, in Bar
    del a
NameError: name 'a' is not defined

Здесь мы видим, что print(a) успешно ищет значение локальной переменной a, а затем на следующей строке вылетает, потому что del не может удалить несвязанную локальную переменную.

1 голос
/ 13 июня 2019

A локальная переменная области действия - это любое имя, определенное где-либо в этой области.Примечательно, что сама переменная является локальной, а не назначенным ей значением - локальная переменная может существовать до или независимо от назначения.

>>> def foo():  # new function scope
...     a = 3    # local variable `a`
...     b: int   # local variable `b`
...     c = 3    # local variable `c`
...     del c
...     print(x)
...     x = 3    # local variable `x`
... foo()
UnboundLocalError: local variable 'x' referenced before assignment

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

Ни один из из ваших примеров не имеет доступа к несвязанной локальной переменной.Оба ищут имя a, но никогда не присваивают ему.


В функциональном блоке ссылка на несвязанные локальные переменные является ошибкой, а именно UnboundLocalError.Не имеет значения, существует ли это имя и во внешней области.

>>> x = 1
>>> def foo():
...     b = x  # local variable is looked up locally
...     x = 2  # make `x` a local variable
... foo()
UnboundLocalError: local variable 'x' referenced before assignment

В блоке класса ссылка на несвязанные локальные переменные возвращается к поиску в глобальной области.Это может или не может быть успешным.

>>> x = 1
>>> class Foo:
...     b = x  # local variable is looked up locally *or globally*
...     x = 2  # make `x` a local variable
... print(Foo.b, Foo.x)
1 2
>>> class Foo:
...     b = y  # local variable is looked up locally *or globally*
...     y = 2  # make `y` a local variable
... print(Foo.b, Foo.y)
NameError: name 'y' is not defined
0 голосов
/ 13 июня 2019

Я думаю, что могу сделать дополнение.

Python предварительно вычисляет, какой фрейм содержит каждое имя, перед выполнением тела функции.

Это означает такое явление:

In [1]: a = 1

In [2]: def test():
   ...:     print(a)
   ...:

In [3]: test()
1

In [4]: def test():
   ...:     print(a)
   ...:     a = 1
   ...:

In [5]: test()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-5-fbd55f77ab7c> in <module>
----> 1 test()

<ipython-input-4-a08051373573> in test()
      1 def test():
----> 2     print(a)
      3     a = 1
      4

UnboundLocalError: local variable 'a' referenced before assignment

In [6]:

Ошибка local variable 'a' referenced before assignment означает, что Python предварительно вычисляет, что функция test frame имеет локальную переменную с именем a, мы должны сначала присвоить один объект a, а затем ссылаться на него позже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...