views:

459

answers:

3

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?

+4  A: 

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
Jonathan Feinberg
In short, you can't use a mutable object as a default value.
S.Lott
Sure you can. Classes are mutable in Python. It has nothing to do with mutable/immutable.
Jonathan Feinberg
Using a mutable object leads to the exact confusion in this question. When you use a class as a default you almost always instantiate to create an object. The mutability of the class doesn't matter because you're not using the class directly they way you use a list or dictionary.
S.Lott
The confusion in the OP has nothing at all to do with "mutability"; it has to do with the incorrect assumption or expectation that the expression in the default clause gets evaluated when the function is invoked, instead of when the function is defined. I don't think you know what the work "mutable" means!
Jonathan Feinberg
+7  A: 

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 dups probably on SO.

SilentGhost
so let's find a dup and close this one?
ax
The issue is this: You cannot use mutable objects as default values. The previous duplicates have all tried to use lists. This is the first using an object. It's the "standard problem" of trying to use a mutable object as a default value.
S.Lott
+3  A: 

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
Robert Rossney