[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Bug in __init__?

Zbigniew Braniecki

1/18/2008 5:10:00 PM

I found a bug in my code today, and spent an hour trying to locate it
and then minimize the testcase.

Once I did it, I'm still confused about the behavior and I could not
find any reference to this behavior in docs.

testcase:

class A():

def add (self, el):
self.lst.extend(el)

def __init__ (self, val=[]):
print val
self.lst = val


def test ():
x = A()
x.add(["foo1","foo2"])
b = A()


So, what I would expect here is that I will create two instances of
class A with empty self.lst property. Right?

In fact (at least with my Python 2.5)

gandalf@gandalf-desktop:~/projects/pyl10n$ ./scripts/test.py
[]
['foo1', 'foo2']

This bug does not happen when I switch to __init__ (self, *args) and
assign self.lst= args[0].

Any clue on what's going on here, and/if where I should report it?

Greetings
Zbigniew Braniecki
17 Answers

Christian Heimes

1/18/2008 5:20:00 PM

0

Zbigniew Braniecki wrote:
> Any clue on what's going on here, and/if where I should report it?

Congratulations! You've stumbled over a well known gotcha. Most newbies
fall for the trap.

class A:
def __init__ (self, val=[]):
print val
self.lst = val

val is created only *once* and shared across all instaces of A.

Christian

eduardo.padoan@gmail.com

1/18/2008 5:21:00 PM

0

On Jan 18, 2008 3:09 PM, Zbigniew Braniecki
<zbigniew.braniecki@gmail.com> wrote:
> I found a bug in my code today, and spent an hour trying to locate it
> and then minimize the testcase.
>
> Once I did it, I'm still confused about the behavior and I could not
> find any reference to this behavior in docs.
>
> testcase:
>
> class A():
>
> def add (self, el):
> self.lst.extend(el)
>
> def __init__ (self, val=[]):
> print val
> self.lst = val
>
>
> def test ():
> x = A()
> x.add(["foo1","foo2"])
> b = A()
>
>
> So, what I would expect here is that I will create two instances of
> class A with empty self.lst property. Right?
>
> In fact (at least with my Python 2.5)
>
> gandalf@gandalf-desktop:~/projects/pyl10n$ ./scripts/test.py
> []
> ['foo1', 'foo2']
>
> This bug does not happen when I switch to __init__ (self, *args) and
> assign self.lst= args[0].
>
> Any clue on what's going on here, and/if where I should report it?

It is a FAQ, not a bug:
http://www.python.org/doc/faq/general/#why-are-default-values-shared-betwe...
http://effbot.org/pyfaq/why-are-default-values-shared-between-o...

--
http://www.advogato.org/person...
Bookmarks: http://del.icio....

Matt McCredie

1/18/2008 5:23:00 PM

0

On Jan 18, 9:09 am, Zbigniew Braniecki <zbigniew.branie...@gmail.com>
wrote:
> I found a bug in my code today, and spent an hour trying to locate it
> and then minimize the testcase.
>
> Once I did it, I'm still confused about the behavior and I could not
> find any reference to this behavior in docs.
>
> testcase:
>
> class A():
>
> def add (self, el):
> self.lst.extend(el)
>
> def __init__ (self, val=[]):
> print val
> self.lst = val
>
> def test ():
> x = A()
> x.add(["foo1","foo2"])
> b = A()
>
> So, what I would expect here is that I will create two instances of
> class A with empty self.lst property. Right?
>
> In fact (at least with my Python 2.5)
>
> gandalf@gandalf-desktop:~/projects/pyl10n$ ./scripts/test.py
> []
> ['foo1', 'foo2']
>
> This bug does not happen when I switch to __init__ (self, *args) and
> assign self.lst= args[0].
>
> Any clue on what's going on here, and/if where I should report it?
>
> Greetings
> Zbigniew Braniecki

Look at item number 5. http://zephyrfalcon.org/labs/python_pit...

People run into this quite often. Basicly, you created a list as a
default argument. This doesn't create a new empty list ever time
__init__ is called. Everyone is getting the same list. How to get
around this:

class A(object): # derive from object for new-style classes
def add (self, el):
self.lst.extend(el)

def __init__ (self, val=None): # Use None instead
if val is None:
val = [] # create a new list if needed each time
print val
self.lst = val


HTH

Matt

mblume

1/18/2008 5:23:00 PM

0

"Zbigniew Braniecki" schrieb
> I found a bug in my code today, and spent an hour trying to locate
it
> and then minimize the testcase.
> [...]
> def __init__ (self, val=[]):
> [...]
> Any clue on what's going on here, and/if where I should report it?
>

I think this has to do with
http://docs.python.org/tut/node6.html#SECTION00671000000...
especially the "Important Warning"

Regards
Martin



Zbigniew Braniecki

1/18/2008 5:33:00 PM

0

Christian Heimes wrote:
> Zbigniew Braniecki wrote:
>> Any clue on what's going on here, and/if where I should report it?
>
> Congratulations! You've stumbled over a well known gotcha. Most newbies
> fall for the trap.
>
> class A:
> def __init__ (self, val=[]):
> print val
> self.lst = val
>
> val is created only *once* and shared across all instaces of A.

Thanks for help guys!

It's really a nice pitfall, I can hardly imagine anyone expecting this,
or how easily could I find this info (e.g. what query should I give to
google to get it without bothering people on this group)

Anyway, thanks :)

Greetings
Zbigniew Braniecki

Fredrik Lundh

1/18/2008 6:11:00 PM

0

Zbigniew Braniecki wrote:

> It's really a nice pitfall, I can hardly imagine anyone expecting this,
> or how easily could I find this info (e.g. what query should I give to
> google to get it without bothering people on this group)

looking things up in the documentation *before* deciding that you might
have done something that nobody's done before is often a good idea:

http://docs.python.org/tut/node6.html#SECTION00671000000...

"Important warning: The default value is evaluated only once.
This makes a difference when the default is a mutable object
such as a list, dictionary, or instances of most classes.
/.../"

http://docs.python.org/ref/fun...

"Default parameter values are evaluated when the function
definition is executed. This means that the expression is
evaluated once, when the function is defined, and that
that same ``pre-computed'' value is used for each call.
This is especially important to understand when a default
parameter is a mutable object, such as a list or a
dictionary /.../

(to be precise, the default values are evaluated when the "def" state-
ment is executed, in the same scope as the "def" statement itself. if
you execute the same "def" statement multiple times, the values are
recalculated.)

</F>

Neil Cerutti

1/18/2008 6:21:00 PM

0

On Jan 18, 2008 12:33 PM, Zbigniew Braniecki
<zbigniew.braniecki@gmail.com> wrote:
> > class A:
> > def __init__ (self, val=[]):
> > print val
> > self.lst = val
> >
> > val is created only *once* and shared across all instaces of A.
>
> Thanks for help guys!
>
> It's really a nice pitfall, I can hardly imagine anyone expecting this,
> or how easily could I find this info (e.g. what query should I give to
> google to get it without bothering people on this group)

In this unfortunate case, useful searches were "default arguments",
which would be hard to guess without already knowing what's going
wrong, or "python gotchas pitfalls", which is a good general purpose
search for when you can't understand what's happening in simple code.

--
Neil Cerutti <mr.cerutti+python@gmail.com>

Bart Ogryczak

1/20/2008 3:34:00 PM

0

On 2008-01-18, citizen Zbigniew Braniecki testified:
> I found a bug in my code today, and spent an hour trying to locate it
> and then minimize the testcase.
>
> Once I did it, I'm still confused about the behavior and I could not
> find any reference to this behavior in docs.
>
> testcase:
>
> class A():
>
> def add (self, el):
> self.lst.extend(el)
>
> def __init__ (self, val=[]):
> print val
> self.lst = val

What you want probably is:
def __init__ (self, val=None):
if(val == None):
self.lst = []
else:
from copy import copy
### see also deepcopy
self.lst = copy(val)

> def test ():
> x = A()
> x.add(["foo1","foo2"])
> b = A()
>
>
> So, what I would expect here is that I will create two instances of
> class A with empty self.lst property. Right?

Wrong. default value for val gets evaluated only once, creating a list
(initialy empty). The same list for all of A's instances.

>>> a = A()
>>> id(a.lst)
13188912

>>> b = A()
>>> id(b.lst)
13188912

Moreover, self.lst = val, does not copy val, rather it creates binding
between self.list and val. So whatever you do to self.list, it affects
val (and vice-versa).

>>> x = []
>>> c = A(x)
>>> id(x)
10508368
>>> id(c.lst)
10508368
>>> c.lst.append('wrong')
>>> x
['wrong']

bart
--
"We're coming in low out of raising sun and about mile up we'll put on music"
http://candajon.azorrag... http://azorragarse.cand...

Bart Ogryczak

1/20/2008 3:40:00 PM

0

On 2008-01-18, citizen Zbigniew Braniecki testified:
> It's really a nice pitfall, I can hardly imagine anyone expecting this,

AFAIR, it's described in Diving Into Python.
It's quiet elegant way of creating cache.

def calculate(x,_cache={}):
try:
return _cache[x]
except KeyError:
_cache[x] = result = _lotsa_slow_calculations(x)
return result

bart
--
This signature is intentionally left blank.
http://candajon.azorrag... http://azorragarse.cand...

Arnaud Delobelle

1/20/2008 4:22:00 PM

0

On Jan 20, 3:39 pm, Bart Ogryczak <B.Ogryc...@addr.in.reply-
to.invalid> wrote:
> On 2008-01-18, citizen Zbigniew Braniecki testified:
>
> > It's really a nice pitfall, I can hardly imagine anyone expecting this,
>
> AFAIR, it's described in Diving Into Python.

Still there seems to be about one message a week about this. Indeed I
reckon the greatest benefit of early binding of default function
arguments is that it attracts lots of new people to comp.lang.python.

> It's quiet elegant way of creating cache.

IMHO, calling it 'elegant' is pushing it too far!

--
Arnaud