python 中关键字参数值的命名空间是什么?

kuc*_* 23 5 python arguments python-3.x

我知道自从我发现 python 以不同的方式对待这个命名空间

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

foo()
foo()
foo([])
foo()
Run Code Online (Sandbox Code Playgroud)

打印以下内容。

[1]
[1,1]
[1]
[1,1,1]
Run Code Online (Sandbox Code Playgroud)

所以我对它们用作对象初始值设定项持怀疑态度。最近我遇到了另一个类似的奇怪行为,如下所示。

class Foo:
    bar = 0
    def __init__(self):
        self.a = bar
Foo()
Run Code Online (Sandbox Code Playgroud)

这会引发异常,因为bar该命名空间内未定义。

class Foo:
   bar = 0
   def __init__(self, a=bar)
       self.a = a
Foo()
Run Code Online (Sandbox Code Playgroud)

现在,这成功地将类变量持有的值分配给初始化器内的foo对象。a为什么会发生这些事情以及如何处理默认参数值?

wim*_*wim 5

三个事实:

  1. 默认参数的名称(左侧)是函数体内的局部变量名称。
  2. 默认参数的值(右侧)在函数定义时在定义函数的范围内计算
  3. 类块中的代码在类定义期间在临时命名空间中执行。类块不被视为封闭范围,如果您期望类似于嵌套的行为,这可能会令人惊讶def

第三点是最微妙的,也许与最初的预期相反。它记录在执行模型中(第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
Run Code Online (Sandbox Code Playgroud)

请注意bar,值0仍然可以作为类属性从方法内访问:通过Foo.barself.bar

您现在应该明白为什么最后一个示例有效

class Foo:
   bar = 0
   def __init__(self, a=bar):
       self.a = a
Foo()
Run Code Online (Sandbox Code Playgroud)

并且,考虑到上面的第 1-3 点,您还应该能够正确预测这里会发生什么:

class Foo:
   def __init__(self, a=bar):
       self.a = a
   bar = 0
Foo()
Run Code Online (Sandbox Code Playgroud)

有关奇怪的类作用域的更多信息,请参见UnboundLocalError: local variable referenced before assignment Why LEGB Rule not apply in this case