From: Jonas Nilsson on
Hello,

Lets say that I want to feed an optional list to class constructor:

class Family():
def __init__(self, fName, members = []):
self.fName = fName
self.members = members

Now, lets add members to two different instances of Family:

f1 = Family("Smith")
f1.members.append("Bill")

f2 = Family("Smithers")
f2.members.append("Joe")

Finally, lets look at the members in the Smithers family:

print f2.members
output: ['Bill', 'Joe']

Why on earth is the output ['Bill', 'Joe']!? Is there a simple
solution that separates f1 and f2 without forcing me to write code for
the special case when you don't feed members to the __init__()-function?

/Jonas
From: Peter Otten on
Jonas Nilsson wrote:

> Lets say that I want to feed an optional list to class constructor:
>
> class Family():
> def __init__(self, fName, members = []):

> Why on earth is the output ['Bill', 'Joe']!? Is there a simple
> solution that separates f1 and f2 without forcing me to write code for
> the special case when you don't feed members to the __init__()-function?

Congrats; you just encountered a very popular new trap ;) The most recent
explanation is only one day old:

http://mail.python.org/pipermail/python-list/2010-August/1252316.html

Peter
From: Benjamin Kaplan on
On Tue, Aug 10, 2010 at 4:58 AM, Jonas Nilsson <jmnc(a)spray.se> wrote:
> Hello,
>
> Lets say that I want to feed an optional list to class constructor:
>
> class Family():
>        def __init__(self, fName, members = []):
>                self.fName = fName
>                self.members = members
>
> Now, lets add members to two different instances of Family:
>
> f1 = Family("Smith")
> f1.members.append("Bill")
>
> f2 = Family("Smithers")
> f2.members.append("Joe")
>
> Finally, lets look at the members in the Smithers family:
>
> print f2.members
> output: ['Bill', 'Joe']
>
> Why on earth is the output ['Bill', 'Joe']!? Is there a simple solution that
> separates f1 and f2 without forcing me to write code for the special case
> when you don't feed members to the __init__()-function?
>
> /Jonas

In python, a function definition is an executable statement, not a
declaration. Default args only get evaluated once- when the function
is first created. You have to use a sentinel value and create the
object inside the function if you want it to get executed every time.


class Family():
def __init__(self, fName, members = None):
if members is None :
members = []
self.fName = fName
self.members = members

If None is a valid argument to this function, then make a dummy object
and check identity (to make sure it's *that* object and not just
something that evaluates equal).

sentinel = object()
def something_that_accepts_none(foo = sentinel) :
if foo is sentinel :
foo = {}
# other stuff
From: Francesco Bochicchio on
On 10 Ago, 13:58, Jonas Nilsson <j...(a)spray.se> wrote:
> Hello,
>
> Lets say that I want to feed an optional list to class constructor:
>
> class Family():
>         def __init__(self, fName, members = []):
>                 self.fName = fName
>                 self.members = members
>
> Now, lets add members to two different instances of Family:
>
> f1 = Family("Smith")
> f1.members.append("Bill")
>
> f2 = Family("Smithers")
> f2.members.append("Joe")
>
> Finally, lets look at the members in the Smithers family:
>
> print f2.members
> output: ['Bill', 'Joe']
>
> Why on earth is the output ['Bill', 'Joe']!? Is there a simple  
> solution that separates f1 and f2 without forcing me to write code for  
> the special case when you don't feed members to the __init__()-function?
>
> /Jonas

You stumbled in two python common pitfalls at once :-)
One, the default arguments issue, was already pointed to you.

The other one is that python variables are just names for objects.
Assigning a variable never mean making a copy, it just means using
another name for the same object.
There used to be a very nice (also graphic) explanationor this
somewhere on the web, but my googling skills failed me this time,
so instead I'll show you the concept using your own code:

>>> class Family:
.... def __init__(self, fName, members = []):
.... self.fname = fName
.... self.members = members
....
>>> mlist = ["Bill"]
>>> f1 = Family("Smiths", mlist )
>>> mlist.append( "John" ) # attempt to not-so-clever reyse of the sme variable
>>> f2 = Family("Smithers", mlist )
>>> f1.members
['Bill', 'John']

Now my example is a bit contrieved but I'm sure you got the idea : in
your example is better to copy the list with
self.members = members[:].

Better yet, you could make use of python arguments grouping feature :
>>> class Family:
.... def __init__(self, fName, *members ):
.... self.members = list(members) # because members is a
tuple
.... self.fname = fName
....
>>> f1 = Family("Smith")
>>> f1.members.append("Bill")
>>> f2 = Family("Smithers")
>>> f2.members.append("Joe")
>>> f2.members
['Joe']
>>> f1.members
['Bill']

This solves your "no initial member" special case and allows for an
easier syntax for creating class instances
(no brackets involved)

>>> f3 = Family("Bochicchio", "Angelo", "Francesco", "Mario")
>>> f3.members
['Angelo', 'Francesco', 'Mario']
>>>


Ciao
----
FB



From: Stefan Schwarzer on
Hi,

On 2010-08-10 17:01, Francesco Bochicchio wrote:
> There used to be a very nice (also graphic) explanationor this
> somewhere on the web, but my googling skills failed me this time,
> so instead I'll show you the concept using your own code:

Probably this isn't the page you're referring to, but I only
recently gave a beginners' talk at EuroPython:

http://sschwarzer.com/download/robust_python_programs_europython2010.pdf

The topic of identity and assignments starts on slide 7,
nice graphics start on slide 10. ;-)

Stefan
 |  Next  |  Last
Pages: 1 2
Prev: freeze function calls
Next: shelf-like list?