class attribute confusion

O

OAN

Hi,

i was having a problem with class attributes initiated outside of
__init__. This code is a demonstration of what i mean:

class A():
mylist = []
def __init__(self):
self.mylist.append(1)
pass

class B(A):
def __init__(self):
A.__init__(self)
self.mylist.append(2)

v = A()
print 'v:',v.mylist
x = B()
print 'x:',x.mylist
y = B()
print 'y:',y.mylist
z = A()
print 'z:',z.mylist
print 'v:',v.mylist

I would expect the following result:

v: [1]
x: [1, 2]
y: [1, 2]
z: [1]
v: [1]

Who wouldn't, right? But actually python 2.6(.6) gives me the following
result:

v: [1]
x: [1, 1, 2]
y: [1, 1, 2, 1, 2]
z: [1, 1, 2, 1, 2, 1]
v: [1, 1, 2, 1, 2, 1]

The four variables v,x,y and z now actually share the same 'mylist'!! To
get the correct results, i have to initialize 'mylist' inside of the
__init__ method!

I think this behaviour is totally wrong, since it seems A.__init__(self)
is changing the value inside of A() not inside of the object variable
'self' (that should be x or y)!!


Could someone explain this to me, please?

regards.
 
A

Arnaud Delobelle

OAN said:
Hi,

i was having a problem with class attributes initiated outside of
__init__. This code is a demonstration of what i mean:

class A():
mylist = []
def __init__(self):
self.mylist.append(1)
pass

class B(A):
def __init__(self):
A.__init__(self)
self.mylist.append(2)

v = A()
print 'v:',v.mylist
x = B()
print 'x:',x.mylist
y = B()
print 'y:',y.mylist
z = A()
print 'z:',z.mylist
print 'v:',v.mylist

I would expect the following result:

v: [1]
x: [1, 2]
y: [1, 2]
z: [1]
v: [1]

Who wouldn't, right? But actually python 2.6(.6) gives me the
following result:

v: [1]
x: [1, 1, 2]
y: [1, 1, 2, 1, 2]
z: [1, 1, 2, 1, 2, 1]
v: [1, 1, 2, 1, 2, 1]

The four variables v,x,y and z now actually share the same 'mylist'!!
To get the correct results, i have to initialize 'mylist' inside of
the __init__ method!

Yes. See below.
I think this behaviour is totally wrong, since it seems
A.__init__(self) is changing the value inside of A() not inside of the
object variable 'self' (that should be x or y)!!

It's not wrong at all. You expect "mylist" to behave as an instance
attribute, but you defined it as a class attribute. Instance attributes
are naturally initialised in the __init__() method.
 
S

Steven D'Aprano

Hi,

i was having a problem with class attributes initiated outside of
__init__. This code is a demonstration of what i mean: [...]
I would expect the following result:

v: [1]
x: [1, 2]
y: [1, 2]
z: [1]
v: [1]

Who wouldn't, right?

Everybody who actually understands Python's object model.

The four variables v,x,y and z now actually share the same 'mylist'!! To
get the correct results, i have to initialize 'mylist' inside of the
__init__ method!

Right. If you define a *class* attribute, it lives in the class, not the
instance, and so all instances share the same value.
I think this behaviour is totally wrong, since it seems A.__init__(self)
is changing the value inside of A() not inside of the object variable
'self' (that should be x or y)!!

A.__init__(self) calls A's init method with self (either x or y) as the
self parameter, but A's init method merely modifies the class attribute
mylist in place. It doesn't create a new list.

The behaviour you're seeing is no different from this:

shared = []
a = {"spam": shared, "ham": 23}
b = {"spam": shared, "ham": 42}
a["spam"].append("parrot")

What would you expect the value of b["spam"] to be?
 
O

Omar Abo-Namous

Am 03.12.2010 23:11, schrieb Arnaud Delobelle:
OAN said:
Hi,

i was having a problem with class attributes initiated outside of
__init__. This code is a demonstration of what i mean:

class A():
mylist = []
def __init__(self):
self.mylist.append(1)
pass

class B(A):
def __init__(self):
A.__init__(self)
self.mylist.append(2)

v = A()
print 'v:',v.mylist
x = B()
print 'x:',x.mylist
y = B()
print 'y:',y.mylist
z = A()
print 'z:',z.mylist
print 'v:',v.mylist

I would expect the following result:

v: [1]
x: [1, 2]
y: [1, 2]
z: [1]
v: [1]

Who wouldn't, right? But actually python 2.6(.6) gives me the
following result:

v: [1]
x: [1, 1, 2]
y: [1, 1, 2, 1, 2]
z: [1, 1, 2, 1, 2, 1]
v: [1, 1, 2, 1, 2, 1]

The four variables v,x,y and z now actually share the same 'mylist'!!
To get the correct results, i have to initialize 'mylist' inside of
the __init__ method!
Yes. See below.
I think this behaviour is totally wrong, since it seems
A.__init__(self) is changing the value inside of A() not inside of the
object variable 'self' (that should be x or y)!!
It's not wrong at all. You expect "mylist" to behave as an instance
attribute, but you defined it as a class attribute. Instance attributes
are naturally initialised in the __init__() method.
Could you please point me to a reference in the doc??

Thanks in advance.
 
S

Steven D'Aprano

Could you please point me to a reference in the doc??

http://docs.python.org/reference/datamodel.html

In the section about classes:

"Class attribute assignments update the class’s dictionary ..."

and in the section about class instances:

"Attribute assignments and deletions update the instance’s dictionary,
never a class’s dictionary."

In this specific example, you also have to realise that mylist.append()
mutates the list in place, and doesn't create a new list. It doesn't
matter whether the list comes from a global variable, a local variable,
an instance attribute or a class attribute, append is always an inplace
operation.
 
S

Steve Holden

Right. If you define a *class* attribute, it lives in the class, not the
instance, and so all instances share the same value.

Unless, of course, an instance binds the same name in its namespace, in
which case it will (usually) mask the class attribute for that instance.

regards
Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top