list in list of objects

Alex Martelli aleax at aleax.it
Tue Mar 4 11:41:06 CET 2003


strachon wrote:

> try this (in python 2.2.2):
> 
>>>> class a:
> u = 0
> v = []

OK: to start with (i.e. until you explicitly rebind either
attribute), all instances of a share the class's u and v
attributes.  OK so far, right?

>>>> x = []
>>>> x.append(a())
>>>> x.append(a())

So you now have two instances of a (sharing the class's
attributes, since there has been no rebinding).  Again,
OK so far, right?

>>>> print x[0].u, x[0].v, x[1].u, x[1].v
> returns: 0 [] 0 []
> 
> now make some changes in properties of the first object:

Careful with that phrase "changes in properties".  There
is no 'property' object involved here (in Python 2.2,
'property' is a built-in -- look it up).  There are two
attributes, named u and v.  And "changes" is also very
ambiguous:

>>>> x[0].u = 1

this is BINDING name u in object x[0] to a new value.
Whatever was previously bound to that NAME in that
object doesn't matter -- re-binding a name (unless you
intervene to alter its semantics, e.g. by __setattr__)
just re-binds that name to a new object, period.

So now x[0] has a per-instance attribute named 'u',
while x[1] doesn't (it still shares the class attribute
of that name).

>>>> x[0].v.append(1)

this does no rebinding at all -- it MUTATES the
object to which x[0].v refers.  The object itself has
mutated; obviously, therefore, all references to that
same object will from now on access the mutated object.

>>>> print x[0].u, x[0].v, x[1].u, x[1].v
> returns: 1 [1] 0 [1]

And indeed, we see that the two ways used here
to refer to the attribute 'v' of class 'a' do
access the same object -- the same list object,
now mutated by having appended a 1 to it.

> i think it is wrong, the last item should be empty

Nope, it's perfectly right.

assert x[0].v is x[1].v is a.v

these are three ways to refer to the SAME object
(a list object).

> i expected something like this: 1 [1] 0 []

Then your understanding of object and references
is imperfect.  Why would you expect x[0].v and
x[1].v to suddenly start referring to DIFFERENT
objects when you haven't re-bound either?

> now append another object to the list 'x':
>>>> x.append(a())
>>>> print x[0].u, x[0].v, x[1].u, x[1].v, x[2].u, x[2].v
> returns: 1 [1] 0 [1] 0 [1]
> 
> it is strange, the last item should be empty again

Nope, x[2].v is, again , the same object as a.v --
and you mutated that earlier.


> is it a bug? if not, how can i get the right behavior?

No bug in Python, and you ARE getting the right
behavior -- not what you WANT, apparently, but
it's a bit difficult to guess what IT is you want.

If you want different instances of a class to
have attributes with the same name that refer to
separate object, you must bind those names to
those objects somewhere -- it will never happen
"by black magic", but only by explicit binding.

The most usual place in which to initialize an
instance's attributes is the __init__ method of
the class.  For example:

class a:
    def __init__(self):
        self.u = 0
        self.v = []

here, class a does not have u and v attributes,
but each INSTANCE of class a does -- all separate,
since we're binding attributes of the INSTANCE,
self, NOT of the whole class.

Such a "class a" would produce exactly the behavior
you say you expect.  It's of course not the only
way to obtain such behavior, but it's quite typical.


Alex





More information about the Python-list mailing list