Static variable vs Class variable

  • Thread starter Bruno Desthuilliers
  • Start date
B

Bruno Desthuilliers

(e-mail address removed) a écrit :
Hi.

I've got a question on the differences and how to define static and
class variables.

What's a "static" variable ? A variable that doesn't move ?-)
> Hence, my understanding is that static variables must be bound to the
class defining the variables

You mean "class attributes". Thinking in terms of another language won't
help understanding Python's objec model.
and shared by children of parent class
where the variable is defined. But, please have a look at this code in
which a guy told me that the variable a is static:

Then don't listen what this guy says.
a = 1
@classmethod
def increment(cls):
cls.a += 1
print cls.a

Here, I am defining variable a which, I believe is class variable,

a class attribute
i.e., variable that is not bound to Foo itself.

Yes it is. It's an attribute of the class object Foo.
Rather, a is bound to
the class which is accessing the variable.
False.
>>> dir(Foo) ['__doc__', '__module__', 'a', 'increment']
>>> Foo.__dict__
{'a': 1, '__module__': '__main__', '__doc__': None, 'increment':
The code that corroborates
this idea is as follows:


???

4

I don't have the same result here. But anyway: the increment method is
rebinding 'a', so the first time it's called on a child class, it
creates an attribute 'a' for this class. Just change your code this way
and you'll see what happens:

def increment(cls):
print dir(cls)
cls.a += 1
print dir(cls)


FWIW, if you don't get this, then you may encounter another common gotcha:

class Truc:
a = 1
def gotcha(self):
self.a += 1
print self.a

print Truc.a
t = Truc()
print t.a
t.gotcha()
print Truc.a
t2 = Truc()
print t2.a
t2.gotcha()
print Truc.a
t.gotcha()
print t.a
print t2.a
print Truc.a

Enjoy !-)

Now back to your snippet : if you don't want this behaviour, wrap 'a'
into a mutable container:

class Foo:
a = [1]

@classmethod
def increment(cls):
cls.a[0] += 1
print cls.a[0]


class Child(Foo):
pass

And if you want to hide that, use properties (warning: you need nex
style classes here):

class Foo(object):
_a = [1]

@classmethod
def increment(cls):
cls._a[0] += 1
print cls._a[0]

@apply
def a():
def fget(self):
return type(self)._a[0]
def fset(self, val):
type(self)._a[0] = val
return property(**locals())

class Child(Foo):
pass
This means that Child1 and Child2 does not share variable a which
means that variable a is class variable rather than static variable.

Nope. This only means that you don't understand the (somewhat peculiar)
semantics of bindings, immutable objects and attribute lookup rules in
Python.
Could you please comment on this? Is a static or class variable?

It's a class attribute.
What's the most recent way of defining 'class' and 'static' variables?

No 'static variable' in Python. And what you call a 'class variable' is
nothing else than an ordinary of the class object
 
B

Bruno Desthuilliers

Marc 'BlackJack' Rintsch a écrit :
L = []
id(L)
3083496716L

L += [1]
id(L)

3083496716L

It's the same L, not rebound at all.

It *is* rebound. To the same object, but it *is* assigned to `L` and not
just mutated in place.

In [107]: class A:
.....: a = list()
.....:

In [108]: class B(A):
.....: pass
.....:

In [109]: B.a += [42]

In [110]: A.a
Out[110]: [42]

In [111]: B.a
Out[111]: [42]

If it was just mutation then `B.a` would have triggered an `AttributeError`.
Nope.
.... l = []
....
>>> class B(A): pass ....
>>> A.l []
>>> A.l += [1]
>>> A.l [1]
>>> B.l [1]
>>>
>>> B.l is A.l
True


And it is *not* rebound:
>>> B.l += [2]
>>> A.l [1, 2]
>>> B.l [1, 2]
>>> A.l is B.l True
>>>
 
B

Bruno Desthuilliers

Bruno Desthuilliers a écrit :
(snip)
And it is *not* rebound:

Doh. Stupid me. Of course it is - but to a ref to the same object...
.... l = []
....
class B(A): pass ....
B.__dict__ {'__module__': '__main__', '__doc__': None}
B.l []
B.l += [1]
B.__dict__ {'__module__': '__main__', '__doc__': None, 'l': [1]}

Thanks Diez for pointing this out. And as far as I'm concerned: time to
bed :(
 
M

minkoo.seo

Hi.

I've got a question on the differences and how to define static and
class variables. AFAIK, class methods are the ones which receives the
class itself as an argument, while static methods are the one which
runs statically with the defining class.

Hence, my understanding is that static variables must be bound to the
class defining the variables and shared by children of parent class
where the variable is defined. But, please have a look at this code in
which a guy told me that the variable a is static:
a = 1
@classmethod
def increment(cls):
cls.a += 1
print cls.a

Here, I am defining variable a which, I believe is class variable,
i.e., variable that is not bound to Foo itself. Rather, a is bound to
the class which is accessing the variable. The code that corroborates
this idea is as follows:
pass

4
pass
4

This means that Child1 and Child2 does not share variable a which
means that variable a is class variable rather than static variable.

Could you please comment on this? Is a static or class variable?
What's the most recent way of defining 'class' and 'static' variables?

Thanks.
- Minkoo
 
L

Laszlo Nagy

Hi.

I've got a question on the differences and how to define static and
class variables. AFAIK, class methods are the ones which receives the
class itself as an argument, while static methods are the one which
runs statically with the defining class.

Hence, my understanding is that static variables must be bound to the
class defining the variables and shared by children of parent class
where the variable is defined. But, please have a look at this code in
which a guy told me that the variable a is static:


a = 1
@classmethod
def increment(cls):
cls.a += 1
print cls.a
In your increment() method, you do this:

cls.a += 1

It does the following thing:

#1. read cls.a
#2. add one
#3. assign this value to cls.a

In point #3, you really bind a name to a value. As you probably know, in
Python, there are names and objects. The initial value of the name 'a'
is 1. It is an immutable object. The "+=" operator usually increments a
value of an object. However, because the 'int' type is immutable, the +=
operator will rather rebind this variable to a newly created value. I
believe this is what is happening here. Your question "is variable a
static or class variable?" has no real answer. After running the
increment() method on a descendant class, e.g. Child1 will rebind the
name Child1.a, creating a new name in the namespace of the class. So the
variable Foo.a is still there, but you are accessing Child1.a instead.

If you want to HANDLE a as a static variable, you can handle it with a
static method. That won't bind a new name in the descendant class.
(However, you can still rebind it, e.g. "Child.a=42")


Now here is a good question: how do you handle a variable as static,
from a class (not static) method? Here is an example:
.... a = 1
.... @classmethod
.... def increment(cls):
.... Foo.a += 1
.... print cls.a
........ pass
....


However, the question is: why would you do this? :)

BTW you should use new style classes whenever it is possible. Old style
classes will have gone...

Hope this helps,

Laszlo
 
M

Marc 'BlackJack' Rintsch

I've got a question on the differences and how to define static and
class variables.

First you have to define what you mean by "static".
AFAIK, class methods are the ones which receives the
class itself as an argument, while static methods are the one which
runs statically with the defining class.

`classmethod`\s receive the class as first arguments, `staticmethod`\s are
just functions bound to the class object.
Hence, my understanding is that static variables must be bound to the
class defining the variables and shared by children of parent class
where the variable is defined. But, please have a look at this code in
which a guy told me that the variable a is static:

Ask the guy what he means by "static".
a = 1
@classmethod
def increment(cls):
cls.a += 1
print cls.a

Here, I am defining variable a which, I believe is class variable,
i.e., variable that is not bound to Foo itself.

No you define a class attribute that *is* bound to the class `Foo`.
Rather, a is bound to the class which is accessing the variable. The code
that corroborates this idea is as follows:

4

Four!? Hard to believe.
4

This means that Child1 and Child2 does not share variable a which means
that variable a is class variable rather than static variable.

Could you please comment on this? Is a static or class variable? What's
the most recent way of defining 'class' and 'static' variables?

There is no such thing as a "static" variable. Think of attributes that
are bound to objects. All dynamically.

What happens is: you bind a 1 to the attribute `Foo.a` in the `Foo` class
definition.

When you call `Child1.increment()` the class method will be called with
`Child1` as first argument. Now ``cls.a += 1`` is executed which is
somewhat like a short form of ``cls.a = cls.a + 1``. So this is reading
the attribute `a` from `Child1` and then bind the result to `Child1`.
`Child1` doesn't have an attribute `a`, so it is looked up in the parent
class. But the result is then bound to `Child1`. So you are reading from
`Foo` and writing to `Child1`. That's it.

Ciao,
Marc 'BlackJack' Rintsch
 
L

Laszlo Nagy

Your question "is variable a
static or class variable?" has no real answer. After running the
increment() method on a descendant class, e.g. Child1 will rebind the
name Child1.a, creating a new name in the namespace of the class. So the
variable Foo.a is still there, but you are accessing Child1.a instead.
Please notice, that theoretically there is no way to "change the value"
of Foo.a in any way, because this is a NAME that references to an
IMMUTABLE OBJECT. It means that you can only rebind the variable. You
cannot change its value, because when we are talking about its value, we
mean the state of the referenced object. The object referenced by the
name is an integer instance, namely it is "one". The object "one" always
remains "one", this cannot be changed. You can add one to one, and you
will get two, but that is another object.

(I'm sorry, probably you already knew this.)

Laszlo
 
M

Matimus

Hi.

I've got a question on the differences and how to define static and
class variables. AFAIK, class methods are the ones which receives the
class itself as an argument, while static methods are the one which
runs statically with the defining class.

Hence, my understanding is that static variables must be bound to the
class defining the variables and shared by children of parent class
where the variable is defined. But, please have a look at this code in
which a guy told me that the variable a is static:


a = 1
@classmethod
def increment(cls):
cls.a += 1
print cls.a

Here, I am defining variable a which, I believe is class variable,
i.e., variable that is not bound to Foo itself. Rather, a is bound to
the class which is accessing the variable. The code that corroborates
this idea is as follows:


4

This means that Child1 and Child2 does not share variable a which
means that variable a is class variable rather than static variable.

Could you please comment on this? Is a static or class variable?
What's the most recent way of defining 'class' and 'static' variables?

In Python `a' is considered a class variable. By modifying `a' using a
class method though, you are essentially re-binding `a' to the class
that it is called from. So, it will be shared by multiple instances of
the same class, but not derivatives. The re-binding only occurs when
`increment' is called. This makes for some very confusing behavior.
I'm not aware of a way, in python, to explicitly specify a variable as
static. For the behavior I think you are looking for you just need to
modify it carefully. This can be done with a static, class or instance
method. Though, using a class method is kind of silly. I'm sure some
would argue that using an instance method is also silly.

code:

class C(object):
a = 0
def inc(self):
C.a += 1 # directly modify the variable on the base class that it is
attached to
return C.a

# or you could use a static method, which is fine since the instance
isn't being used. This
# will also allow the method to be called without instantiating the
class.

class C(object):
a = 0
@staticmethod
def inc():
C.a += 1
return C.a

Matt
 
D

Diez B. Roggisch

In point #3, you really bind a name to a value. As you probably know, in
Python, there are names and objects. The initial value of the name 'a'
is 1. It is an immutable object. The "+=" operator usually increments a
value of an object. However, because the 'int' type is immutable, the +=
operator will rather rebind this variable to a newly created value. I
believe this is what is happening here.

Your believes aside, this is simply wrong. The statement

a += x

always leads to a rebinding of a to the result of the operation +. I
presume you got confused by the somewhat arbitrary difference between

__add__

and


__iadd__

that somehow suggest there is an in-place-modification going on in case
of mutables.

but as the following snippet shows - that's not the case:


class Foo(object):
def __add__(self, o):
return "__add__"

def __iadd__(self, o):
return "__iadd__"


a = Foo()
a += 1
print a

a = Foo()
b = Foo()
c = a + b
print c

So you see, the first += overrides a with the returned value of __iadd__.

The reason for the difference though is most probably what you yourself
expected: thus it's possible to alter a mutable in place.

Diez
 
S

Steven D'Aprano

Your believes aside, this is simply wrong. The statement

a += x

always leads to a rebinding of a to the result of the operation +.

Not true.
L = []
id(L) 3083496716L
L += [1]
id(L)
3083496716L

It's the same L, not rebound at all.


I presume you got confused by the somewhat arbitrary difference between

__add__

and

__iadd__

that somehow suggest there is an in-place-modification going on in case
of mutables.

The __iFOO__ methods are supposed to do in-place modification if
possible, but it is up to the class writer to make them do so. In the
case of your example, you specifically created an __iadd__ method that
didn't even attempt in-place modification. What did you expect it to do?

but as the following snippet shows - that's not the case:


class Foo(object):
def __add__(self, o):
return "__add__"

def __iadd__(self, o):
return "__iadd__"


a = Foo()
a += 1
print a

a = Foo()
b = Foo()
c = a + b
print c

So you see, the first += overrides a with the returned value of
__iadd__.

That's because you told it to do that. If you told it to do something
more sensible, it would have done so. Lists know how to do in-place
modification:
3083496972L
 
L

Laszlo Nagy

Steven said:
Not true.
Hmm. Or you can write __iadd__ to rebind the name to the same object
again? I think usually there is not much difference between "rebinding
to the same object" and "not rebinding". But as we have seen, sometimes
there is a difference. I think that Diez made his statement because it
is possible to rebind from __iadd__ to a different object. So it is more
safe to think that "inplace operations" will always rebind, than to
think they will never rebind.

I'm not sure about the language level. It is told that "+=" is an
inplace operator, but how "in place" is defined? If we say "an inplace
operator will change the state of an object, and will never create a new
object" then "+=" is NOT an inplace operator. Well, at least not when
you watch it from the language level. But what about the implementation
level? Here is an example:

a = 1
a+= 1 # The compiler will probably optimize this and the Python bytecode
interpreter will not rebind 'a' here, just increment the integer in memory.

This topic starts to be very interesting. Language lawyers and
developers, please help us! How "in place operator" is defined in
Python? What makes it an "in place operator"? Is it the syntax, or what?

Laszlo
 
M

Marc 'BlackJack' Rintsch

L = []
id(L) 3083496716L
L += [1]
id(L)
3083496716L

It's the same L, not rebound at all.

It *is* rebound. To the same object, but it *is* assigned to `L` and not
just mutated in place.

In [107]: class A:
.....: a = list()
.....:

In [108]: class B(A):
.....: pass
.....:

In [109]: B.a += [42]

In [110]: A.a
Out[110]: [42]

In [111]: B.a
Out[111]: [42]

If it was just mutation then `B.a` would have triggered an `AttributeError`.

Ciao,
Marc 'BlackJack' Rintsch
 
D

Diez B. Roggisch

Steven said:
Not true.


Yes, it is.
L = []
id(L) 3083496716L
L += [1]
id(L)
3083496716L

It's the same L, not rebound at all.

Just because the __iadd__-operation in the list implemented chose to
returen a reference to itself - certainly a sensible choice, I never
doubted that - doesn't change the fact that python rebinds the name on
the left with whatever __iadd__ returned.

Diez
 
S

Steven D'Aprano

L = []
id(L) 3083496716L
L += [1]
id(L)
3083496716L

It's the same L, not rebound at all.

It *is* rebound. To the same object, but it *is* assigned to `L` and
not just mutated in place.

Picky picky.

Yes, technically there is an assignment of L to itself. I was sloppy to
say "not rebound at all", because when you write an augmented assignment
method you have to return self if you want to implement in-place
mutation. But I hardly call "rebinding to itself" any sort of rebinding
worth the name :)

Diez is still wrong though, even though I overstated my case. See my
reply to his post.



[snip code]
If it was just mutation then `B.a` would have triggered an
`AttributeError`.

Why? Don't Python classes have inheritance?

(That's a rhetorical question. Yes they do, and no B.a would not raise
AttributeError because it would inherit from A.)
 
S

Steven D'Aprano

Yes, it is.

I'm afraid not.

As I admitted in my reply to Marc, I overstated my case by saying that L
isn't rebound at all. Of course it is rebound, but to itself.

However, it is not true that += "always leads to a rebinding of a to the
result of the operation +". The + operator for lists creates a new list.
+= for lists does an in-place modification:
[1]

Compare with:
[]

You said:

"I presume you got confused by the somewhat arbitrary difference between
__add__ and __iadd__ that somehow suggest there is an in-place-
modification going on in case of mutables but as the following snippet
shows - that's not the case: ..."

That's an explicit denial that in-place modification takes place, and
that's *way* off the mark. I was concentrating so hard on showing in-
place modification that I glossed over the "return self" part.
 
S

Steven D'Aprano

a = 1
a+= 1 # The compiler will probably optimize this and the Python bytecode
interpreter will not rebind 'a' here, just increment the integer in
memory.

No. This is Python, not C. You can't increment integers in memory.
Integers are immutable objects, not raw bytes:
150830884
 
M

Marc 'BlackJack' Rintsch

L = []
id(L)
3083496716L
L += [1]
id(L)
3083496716L

It's the same L, not rebound at all.

It *is* rebound. To the same object, but it *is* assigned to `L` and
not just mutated in place.

Picky picky.

Yes, technically there is an assignment of L to itself. I was sloppy to
say "not rebound at all", because when you write an augmented assignment
method you have to return self if you want to implement in-place
mutation. But I hardly call "rebinding to itself" any sort of rebinding
worth the name :)

Maybe picky but that detail was the source of the OPs confusion because it
introduced a new attribute on the subclass.

Ciao,
Marc 'BlackJack' Rintsch
 
D

Diez B. Roggisch

Yes, it is.
I'm afraid not.

As I admitted in my reply to Marc, I overstated my case by saying that L
isn't rebound at all. Of course it is rebound, but to itself.

However, it is not true that += "always leads to a rebinding of a to the
result of the operation +". The + operator for lists creates a new list.
+= for lists does an in-place modification:


It still is true.

a += b

rebinds a. Period. Which is the _essential_ thing in my post, because
this rebinding semantics are what confused the OP.



L = []
M = L
L += [1]
M
[1]

Compare with:
L = []
M = L
L = L + [1]
M
[]

You said:

"I presume you got confused by the somewhat arbitrary difference between
__add__ and __iadd__ that somehow suggest there is an in-place-
modification going on in case of mutables but as the following snippet
shows - that's not the case: ..."

Admittedly, I miss _one_ word here: necessarily before the "an".
That's an explicit denial that in-place modification takes place, and
that's *way* off the mark. I was concentrating so hard on showing in-
place modification that I glossed over the "return self" part.

And I was concetrating so hard on the rebinding-part, I glossed over the
in-place-modification part.

Diez
 
D

Diez B. Roggisch

Diez said:
It still is true.

a += b

rebinds a. Period. Which is the _essential_ thing in my post, because
this rebinding semantics are what confused the OP.

Which I just came around to show in a somewhat enhanced example I could
have used the first time:

class Foo(object):

a = []

@classmethod
def increment(cls):
print cls
cls.a += [1]

class Bar(Foo):
pass

Foo.increment()
Bar.increment()

print Foo.a
print Bar.a
Bar.a = []
print Foo.a
print Bar.a



192:~/projects/SoundCloud/ViewAnimationTest deets$ python /tmp/test.py
<class '__main__.Foo'>
<class '__main__.Bar'>
[1, 1]
[1, 1]
[1, 1]
[]


Which makes the rebinding-part of __iadd__ pretty much an issue, don't
you think?


Diez
 
L

Laszlo Nagy

Steven D'Aprano írta:
No. This is Python, not C. You can't increment integers in memory.
What? Please read my post. I was talking about __implementation level__,
clearly. :)
 

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,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top