views:

97

answers:

3

I have some trouble understanding what happens with class init arguments that are lists
like:

class A(object):
    def __init__(self, argument=[]):  
        self.argument = argument[:]  

or:

def __init__(self,argument=None):  
    self.arguments = arguments or []  

or:

def __init__(self, argument=[]):  
    self.argument = argument  

This can't be done because the default value for every A object would point to the same piece of memory. I can't really understand what happens here and how it happens.

+4  A: 

The list is created when the definition is run, not when the method is executed.

Ignacio Vazquez-Abrams
+1  A: 

I think the question is somewhat unclear, but what I understand from it is that you are wondering how passing a class init a list doesn't make the internal object the same one you pass it, but rather a copy. If this is not the question, tell me, and I'll delete this answer.

Briefly, here's how it works: nothing is stored as a name by value in Python. Everything is stored internally as a pointer to a Python object. When you do:

a = [1,2,3]
b = a

You're not setting b to the value of a. You're setting b to reference the same object. So the statement:

a is b

Is true, as the names reference the same object.

a = [1,2,3]
b = [1,2,3]
a is b

This will return false. The reason is that now you have created two different objects. So the line:

self.argument = argument[:]

is a (necessary) way of making a copy of self.argument so that it doesn't reference the same object.

avpx
sorry for my bad english I guess that's why I was missunderstood,your answer helped anyways but the real question is why in the last example every new object made with no initializer would have the same list ( same object)for example if I create objects b, c, and d of type a without arguments the argument member of b,c and d would be the same (shared)
damian
Oh, I understand now. Yeah, this is one of the more common "blunders" in Python. Google "python default argument" and you'll see. The correct way to do what you're trying to do is to make the default argument None and then do a check against None, creating the list at that time.
avpx
Great I understand everything now the [] is evaluated when defining not when creating every object. (like the first comment said but at that moment I didin't understand it at all hehe)Thank you! this is the first time I use this site I had never got such a fast answer before! ( actually I never get any response on forums)
damian
+3  A: 

This is a well known python gotcha

Basically, the default for that argument is created when the method is first defined, and since it is a mutable object (in this case, a list), it just referes to the same object even after it has changed, and even in subsequent calls to the method.

The usual way to deal with cases like that is to treat it like in your second example:

def __init__(self,argument=None):  
    self.arguments = arguments or []

But if what you want to do is have a list with all your arguments you could just use Python's argument unpacking.

It works like this:

def my_function(*args):
    print args

then, you will have in your method access to a tuple with all the arguments passed. So if you called your function like this:

>>> my_function(1, 2, 3)

your output would be:

(1, 2, 3)

The cool thing is, you can always use it in the oposite way, so, lets suppose you have a list (or tuple), and you want to pass every item in the list as a positional argument to your function. You would do this:

>>> my_list = [1, 2, 3]
>>> my_function(*my_list)

And, as far as your function is concerned, it is the same as the previous call.

You should read the documentation that I pointed to, as well as the section that goes a little more deeply on defining functions.

Flávio Amieiro
Thanks!, useful info too, I think I'm starting to like python :D
damian