简体   繁体   中英

What is a keyword argument value's namespace in python?

I know python treats this namespace in a different way ever since I found out about

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

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

which prints the following.

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

So I was sceptical about their use as an object initializers. Then recently I encountered another similarly strange behaviour, demonstrated below.

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

This raises an exception as bar is not defined inside this namespace.

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

Now this successfully assigns value held by a class variable foo to an object a inside the initializer. Why do these things happen and how are the default argument values treated?

Three facts:

  1. The name (left side) of a default argument is a local variable name inside the function body.
  2. The value (right side) of a default argument is evaluated in the scope where the function is defined, at the time of function definition.
  3. Code within a class block is executed in a temporary namespace during class definition. The class block is not treated like an enclosing scope, which may be surprising if you were expecting behavior similar to a nested def .

Point 3 is the most subtle and perhaps contrary to initial expectations. It's documented in the execution model (section 4.2.2. Resolution of names ):

The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods

This is why the name bar is not resolved in your second example:

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

Note that bar value, 0 , would still be accessible from within the method as a class attribute: via either Foo.bar or self.bar .

You should now understand why the final example does work:

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

And, considering points 1-3 above, you should also be able to correctly predict what happens here:

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

There's more information about the weird class scope over in UnboundLocalError: local variable referenced before assignment why LEGB Rule not applied in this case .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM