Guys, I just started python recently and get confused with the optional parameters, say I have the program like this:
class B:
pass
class A:
def __init__(self, builds = B()):
self.builds = builds
If I create A twice
b = A()
c = A()
and print their builds
print b.builds
print c.builds
I found they are using the exactly same object,
<__main__.B instance at 0x68ee0>
<__main__.B instance at 0x68ee0>
But it is not what I want, since if b
changed some internal state of builds, the one in c
object will also be changed.
Is it possible to recreate this optional parameters each time by using this optional parameters syntax?
You need to understand how default values work in order to use them effectively.
Functions are objects. As such, they have attributes. So, if I create this function:
>>> def f(x, y=[]):
y.append(x)
return y
I've created an object. Here are its attributes:
>>> dir(f)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__',
'__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals',
'func_name']
One of them is func_defaults
. That sounds promising, what's in there?
>>> f.func_defaults
([],)
That's a tuple that contains the function's default values. If a default value is an object, the tuple contains an instance of that object.
This leads to some fairly counterintuitive behavior if you're thinking that f
adds an item to a list, returning a list containing only that item if no list is provided:
>>> f(1)
[1]
>>> f(2)
[1, 2]
But if you know that the default value is an object instance that's stored in one of the function's attributes, it's much less counterintuitive:
>>> x = f(3)
>>> y = f(4)
>>> x == y
True
>>> x
[1, 2, 3, 4]
>>> x.append(5)
>>> f(6)
[1, 2, 3, 4, 5, 6]
Knowing this, it's clear that if you want a default value of a function's parameter to be a new list (or any new object), you can't simply stash an instance of the object in func_defaults
. You have to create a new one every time the function is called:
>>>def g(x, y=None):
if y==None:
y = []
y.append(x)
return y
you need to do the following:
class A:
def __init__(self, builds=None):
if builds is None:
builds = B()
self.builds = builds
it's a very wide-spread error, using mutable parameters as a default arguments. there are plenty of dupes probably on SO.
Yes; default parameters are evaluated only at the time when the function is defined.
One possible solution would be to have the parameter be a class rather than an instance, a la
def foo(blah, klass = B):
b = klass()
# etc