Что такое пространство имен значения аргумента ключевого слова в python? - PullRequest
4 голосов
/ 10 октября 2019

Я знаю, что python трактует это пространство имен по-другому с тех пор, как узнал о

def foo(l=[]):
    l.append(1)
    print(l)

foo()
foo()
foo([])
foo()

, который печатает следующее.

[1]
[1,1]
[1]
[1,1,1]

Так что я скептически относился к их использованию какинициализаторы объекта. Затем недавно я столкнулся с другим, столь же странным поведением, продемонстрированным ниже.

class Foo:
    bar = 0
    def __init__(self):
        self.a = bar
Foo()

Это вызывает исключение, поскольку bar не определено в этом пространстве имен.

class Foo:
   bar = 0
   def __init__(self, a=bar)
       self.a = a
Foo()

Теперь это успешно присваивает значениехранится в переменной класса foo объекта a внутри инициализатора. Почему это происходит и как обрабатываются значения аргументов по умолчанию?

1 Ответ

4 голосов
/ 10 октября 2019

Три факта:

  1. имя (левая сторона) аргумента по умолчанию - это имя локальной переменной внутри тела функции.
  2. значение (правая сторона) аргумента по умолчанию оценивается в области, в которой определена функция, во время определения функции.
  3. Код в блоке класса выполняется во временном пространстве имен во время классаопределение. Блок класса не рассматривается как охватывающая область, что может удивить, если вы ожидаете поведение, подобное вложенному def.

Точка 3 является наиболее тонкой и, возможно, противоречит первоначальным ожиданиям. Это задокументировано в модели исполнения (раздел 4.2.2. Разрешение имен ):

Область имен, определенных в блоке класса, ограниченаблок класса;оно не распространяется на кодовые блоки методов

Вот почему имя bar не разрешено во втором примере:

class Foo:
    bar = 0
    def __init__(self):
        self.a = bar  # name "bar" isn't accessible here, but code is valid syntax

Foo()  # NameError: name 'bar' is not defined

Обратите внимание, что значение bar, 0, все равно будет доступен изнутри метода как атрибут класса: через Foo.bar или self.bar.

Теперь вы должны понять, почему последний пример работает :

class Foo:
   bar = 0
   def __init__(self, a=bar):
       self.a = a
Foo()

И, учитывая пункты 1-3 выше, вы также должны быть в состоянии правильно предсказать, что здесь происходит:

class Foo:
   def __init__(self, a=bar):
       self.a = a
   bar = 0
Foo()

Более подробная информация о странной области видимости приведена в UnboundLocalError: локальная переменная, на которую ссылается перед присваиванием, почему в этом случае правило LEGB не применяется .

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