简体   繁体   中英

Python - inbound outbound argument

I've read in Expert Python Programming about this edge case. Check this code:

def f(arg={}):
    arg['3'] = 4
    return arg

>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4, '4': 'Still here'}

It's not clear to me why when f gets called the last time (after its return value has been saved), instead of assigning arg the empty dict (since it was called with no arguments), it keeps the old reference.

The book says so: "if an object is created within the arguments, the argument reference will still be alive if the function returns the object".

I understand that "this is the way it works", but why so?

Your problem is defaulting to a mutable argument (dictionary in this case):

def f(arg={}):
    arg['3'] = 4
    return arg

should be:

def f(arg=None):
    arg = arg if arg is not None else {}
    arg['3'] = 4
    return arg

yields:

>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4}

like you'd expect.

The issue here is the default arguments are evaluated when the function is first defined/parsed, not when they are called. It's just a nuance of the python parser that you need to be aware of.

For the why, check out "Least Astonishment" and the Mutable Default Argument

Because the default arguments are evaluated once only, when the function is evaluated and created (they are part of the function defenition and can be fetched through inspect.getargspec, for example).

Since they are part of the function, every call to the function will have the same instance of the default value. This is not a problem if it is an immutable value, but as soon as it is mutable it can become a gotcha.

The same 'feature' exist in class defenitions, given a class defenition:

class A(object):
    foo = {}

calling

x = A() 
y = A()
x.foo['bar'] = "baz"

...would give that y.foo['bar'] evaluates to "baz", since x and y has the same foo. This is why member initialization should be done in init instead of the class body.

The default parameter gets created once, when the functon is declared, so every call to f() gets the same instance of a dictionary, which starts out empty. Does that answer the question?

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