Unclear On Class Variables

T

Tim Daneliuk

I am a bit confused. I was under the impression that:

class foo(object):
x = 0
y = 1

means that x and y are variables shared by all instances of a class.
But when I run this against two instances of foo, and set the values
of x and y, they are indeed unique to the *instance* rather than the
class.

It is late and I am probably missing the obvious. Enlightenment appreciated ...
 
D

Diez B. Roggisch

Tim said:
I am a bit confused. I was under the impression that:

class foo(object):
x = 0
y = 1

means that x and y are variables shared by all instances of a class.
But when I run this against two instances of foo, and set the values
of x and y, they are indeed unique to the *instance* rather than the
class.

It is late and I am probably missing the obvious. Enlightenment
appreciated ...

Without actual code how you set the vars, no one can answer that question.
 
S

Simon Brunning

I am a bit confused. I was under the impression that:

class foo(object):
x = 0
y = 1

means that x and y are variables shared by all instances of a class.
But when I run this against two instances of foo, and set the values
of x and y, they are indeed unique to the *instance* rather than the
class.

I can see why you might think that:
.... eggs = 4
.... 4

But you are being mislead by the fact that integers are immutable.
'spam.eggs = 2' is *creating* an instance member - there wasn't one
before. Have a look at what happens with a mutable object:
.... eggs = [3]
....
spam = Spam()
spam2 = Spam()
spam.eggs [3]
spam2.eggs [3]
spam.eggs.append(5)
spam.eggs [3, 5]
spam2.eggs
[3, 5]
 
H

harold fellermann

Hi Tim,

If you have

class Foo(object) :
x = 0
y = 1

foo = Foo()

foo.x # reads either instance or class attribute (class in this case)

foo.x = val # sets an instance attribute (because foo is instance not
class)

Foo.x = val # sets a class attribute
foo.__class.__x = val # does the same

this might be sometimes confusing. IMHO, the following is especially
nasty:
0

although the += operator looks like an inplace add it isn't.
it is just syntactic sugar for foo.x = foo.x + 1.


- harold -
 
A

Antoon Pardon

Op 2005-01-13 said:
I am a bit confused. I was under the impression that:

class foo(object):
x = 0
y = 1

means that x and y are variables shared by all instances of a class.
But when I run this against two instances of foo, and set the values
of x and y, they are indeed unique to the *instance* rather than the
class.

I can see why you might think that:
... eggs = 4
... 4

But you are being mislead by the fact that integers are immutable.
'spam.eggs = 2' is *creating* an instance member - there wasn't one
before. Have a look at what happens with a mutable object:
... eggs = [3]
...
spam = Spam()
spam2 = Spam()
spam.eggs [3]
spam2.eggs [3]
spam.eggs.append(5)
spam.eggs [3, 5]
spam2.eggs
[3, 5]

Well I find this a confusing behaviour on python's part. The fact
that instance.field can mean something different, depending on
where in a statement you find it, makes the behaviour inconsistent.

I know people in general here are against declarations, but declarations
could IMO provide more consistency here and thus more obvious behaviour.
 
A

Antoon Pardon

Op 2005-01-13 said:
Hi Tim,

If you have

class Foo(object) :
x = 0
y = 1

foo = Foo()

foo.x # reads either instance or class attribute (class in this case)

foo.x = val # sets an instance attribute (because foo is instance not
class)

Foo.x = val # sets a class attribute
foo.__class.__x = val # does the same

this might be sometimes confusing. IMHO, the following is especially
nasty:

0

although the += operator looks like an inplace add it isn't.
it is just syntactic sugar for foo.x = foo.x + 1.

Except is x belongs to a mutable class that implements the
+= operator as an inplace add.

Try the same but with x = [2]
and foo.x += [3]
 
P

Peter Hansen

Simon said:
But you are being mislead by the fact that integers are immutable.
'spam.eggs = 2' is *creating* an instance member - there wasn't one
before. Have a look at what happens with a mutable object:

Simon, it's really not about mutability at all. You've changed
the example, which was binding a name (specifically setting an
attribute), to one in which you are simply calling a method on
the object. If you change your example to bind the name the
same way, even with a mutable, it will work the same way as Tim's
original did with integers:
.... eggs = [3]
....
spam = Spam()
spam2 = Spam()
spam.eggs = [7]
>>> spam2.eggs
[3]

-Peter
 
S

Simon Brunning

Simon, it's really not about mutability at all. You've changed
the example,

Err, there *wasn't* an example, not really. The OP just mentioned
'setting the values' of instance members. That *can* mean name
binding, but (to my mind at least) it can also mean calling mutating
methods. I just wanted to show both.
 
A

Antoon Pardon

Well I find this a confusing behaviour on python's part. The fact
that instance.field can mean something different, depending on
where in a statement you find it, makes the behaviour inconsistent.

I know people in general here are against declarations, but declarations
could IMO provide more consistency here and thus more obvious behaviour.

Well just to show how confusing python can be, the following piece of
code.

| class Spam:
| eggs = [2, 3]
|
|
| sp1 = Spam()
| sp2 = Spam()
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'
|
| sp1.eggs += [4,]
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'
|
| Spam.eggs = [3,5]
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'

Which produces:

[2, 3] 1075958860
[2, 3] 1075958860
 
S

Steve Holden

Tim said:
I am a bit confused. I was under the impression that:

class foo(object):
x = 0
y = 1

means that x and y are variables shared by all instances of a class.

What it actually does is define names with the given values *in the
class namespace*.
But when I run this against two instances of foo, and set the values
of x and y, they are indeed unique to the *instance* rather than the
class.
I imagine here you are setting instance variables, which then *mask* the
presence of class variables with the same name, because "self-relative"
name resolution looks in the instance namespace before it looks in the
class namespace.
It is late and I am probably missing the obvious. Enlightenment
appreciated ...

You can refer to class variables using the class name explicitly, both
within methods and externally:
... count = 0
... def getCt(self):
... return self.count
... def inc(self):
... self.count += 1
...
regards
Steve
 
F

Fredrik Lundh

Tim said:
I am a bit confused. I was under the impression that:

class foo(object):
x = 0
y = 1

means that x and y are variables shared by all instances of a class.
But when I run this against two instances of foo, and set the values
of x and y, they are indeed unique to the *instance* rather than the
class.

"set" as in:

obj = foo()
obj.x = 10 # set x

?

if so, the "obj.x=" line is *adding* an instance variable to the "x" object, which will
then hide the "x" at the class level.
.... x = 0
.... y = 1
....0

if you want to assign to the class variable, assign to the class variable:
20

</F>
 
P

Pierre Barbier de Reuille

Antoon Pardon a écrit :
Well I find this a confusing behaviour on python's part. The fact
that instance.field can mean something different, depending on
where in a statement you find it, makes the behaviour inconsistent.

I know people in general here are against declarations, but declarations
could IMO provide more consistency here and thus more obvious behaviour.


Well just to show how confusing python can be, the following piece of
code.

| class Spam:
| eggs = [2, 3]
|
|
| sp1 = Spam()
| sp2 = Spam()
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'
|
| sp1.eggs += [4,]
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'
|
| Spam.eggs = [3,5]
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'

Which produces:

[2, 3] 1075958860
[2, 3] 1075958860

Well ... and could someone explain this behaviour ?
I don't catch it !

Pierre
 
P

Pierre Barbier de Reuille

Pierre Barbier de Reuille a écrit :
Antoon Pardon a écrit :
Well I find this a confusing behaviour on python's part. The fact
that instance.field can mean something different, depending on
where in a statement you find it, makes the behaviour inconsistent.

I know people in general here are against declarations, but declarations
could IMO provide more consistency here and thus more obvious behaviour.



Well just to show how confusing python can be, the following piece of
code.

| class Spam:
| eggs = [2, 3]
| | | sp1 = Spam()
| sp2 = Spam()
| | print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'
| | sp1.eggs += [4,]
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'
|
| Spam.eggs = [3,5]
|
| print sp1.eggs, id(sp1.eggs)
| print sp2.eggs, id(sp2.eggs)
| print '--------------------'

Which produces:

[2, 3] 1075958860
[2, 3] 1075958860

Well ... and could someone explain this behaviour ?
I don't catch it !

Pierre

Ok, I think I got it ! I speak with friends working with Python too ...
It seems that "a += l" if "a" and "l" are lists is equivalent to :

a.extend(l)
a = a

The second line could seem meaningless but it is not ! Indeed, in the
example above, the first "sp1.eggs" (the one with the extend) is a class
variable but, the second "sp1.eggs" (the one before the "=") is an
instance variable !

So, at the end, we append to get sp1.eggs and Spam.eggs references to
the same structure. But sp1.eggs is an instance variable of sp1 and no
more the class variable. To test that, it's possible to modify slightly
the code with :

|sp1.eggs += [4,]
|del sp1.eggs

Then, sp1.eggs still exists !!! But it's again the class variable ...

Ok, back to the documentation ...

In the doc, there is a special case for the use of "+=" with the class
members. IMHO, this should not be !!! But, it says that :

ob.a += b

is translated into :

ob.__setattr__( "a", ob.__getattr__("a").__iadd__(b) )

My opinion is : it would be much more simpler to explain than :

a += b <=> a.__iadd__(b); a = a

and not give any special case for class members. In both cases, the
resulting behaviour is the same, but it would be less confusing.

Then, this change of scope of variables in python is very very annoying.
Both for new and old programmers (we have both in my lab ...).

Well, I hope I got it write this time ... but this is a feature to fear !!!

Pierre
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top