3

I am trying to change a static variable in python

>>> class A():
...     i = 0
...     def add_i(self):
...             self.i = self.i + 1
...
>>> A.i
0
>>> a = A()
>>> a.add_i()
>>> A.i
0
>>> a.i
1

When I call a.add_i(), why is it not incrementing the 'static' variable i?

1
  • Hello. May I know if you have read my answer, and if yes why you think it doesn't deserve to be upvoted ? I am not chasing upvotes, I would like to understand why an answer that explain precisely the things is so much neglected, not only by other users, since no one did upvote it, but even no more by the person who asked the question. Thank you. Commented Jan 5, 2014 at 17:35

6 Answers 6

11

When you assign to self.i, you are creating a new instance variable called i:

>>> print id(A.i), id(a.i)
9437588 9437576

The following will change A.i instead of rebinding a.i:

A.i = A.i + 1

or, shorter:

A.i += 1
Sign up to request clarification or add additional context in comments.

3 Comments

Excuse me, but I find "creating a new instance variable called i" is an improper way to express. 1/ if the variable is called i, it means that the word variable designates an object. That's a wrong way because objects in Python are not variables in the common sense used in other languages. 2/ it gives the impression that what is created is a new object and that it is binded to the name i, without knowing if, in your mind, the name i was already present as a key in the instance's _dict__ or if it wasn't and then is also created to be binded to the new object.
@NPE: A.i = A.i + 1 from inside A? You can reference A with A from inside A?
@Johnston Yes. One cannot reference i from inside the function because the scope of names defined at the class-level doesn't extend inside the methods. But one can reference i through A.i because the function wrapped by the method goes out of itself and search for the identifier A in the global namespace. After having found it, it searches i in the namespace of A
4

The following code shows precisely what happens:

class A():
    i = 0
    def add_i(self):
        print '\n-----In add_i function-----'
        print 'BEFORE: a.__dict__ :',a.__dict__
        print 'BEFORE: id(self)   : %d\n' % id(self.i)
        self.i = self.i + 1
        print 'self.i = self.i + 1  done\n'
        print 'AFTER: a.__dict__ :',a.__dict__
        print 'AFTER: id(self)   : %d' % id(self.i)
        print '-----end of add_i function-----\n'

a = A()

print '\nA.i     ==',A.i
print 'id(A.i) ==',id(A.i)
print 'A.__dict__.keys() :',A.__dict__.keys()

print '\na.i     ==',a.i
print 'id(a.i) ==',id(a.i)
print 'a.__dict__.keys() :',a.__dict__.keys()

a.add_i()

print '\nA.i     ==',A.i
print 'id(A.i) ==',id(A.i)
print 'A.__dict__.keys() :',A.__dict__.keys()

print '\na.i     ==',a.i
print 'id(a.i) ==',id(a.i)
print 'a.__dict__.keys() :',a.__dict__.keys()

result

A.i     == 0
id(A.i) == 10021948
A.__dict__.keys() : ['i', 'add_i', '__module__', '__doc__']

a.i     == 0
id(a.i) == 10021948
a.__dict__.keys() : []

-----In add_i function-----
BEFORE: a.__dict__ : {}
BEFORE: id(self)   : 10021948

self.i = self.i + 1  done

AFTER: a.__dict__ : {'i': 1}
AFTER: id(self)   : 10021936
-----end of add_i function-----


A.i     == 0
id(A.i) == 10021948
A.__dict__.keys() : ['i', 'add_i', '__module__', '__doc__']

a.i     == 1
id(a.i) == 10021936
a.__dict__.keys() : ['i']

The __dict__ of an object exposes the namespace of the object, that is to say a mapping from names to objects holding its attributes.

Just after the class A and its instancea have been created, only A has an attribute i,
a doesn't.
In fact at this moment, the namespace of a is void, and when the intepreter encounters a.i it does that:

A class instance has a namespace implemented as a dictionary which is the first place in which attribute references are searched. When an attribute is not found there, and the instance’s class has an attribute by that name, the search continues with the class attributes. see 3 Data model 3.2 The standard type hierarchy
in the Python Language Reference

That is to say, as i isn't an a instance's attribute, the interpreter goes and search in the namespace of A: there it finds an attribute i and it returns this attribute as the result of the call of a.i

.

Now, when a.add_i() is executed, the instruction self.i = self.i + 1 is processed like follows:

  • firstly, self.i is searched: as i isn't an attribute of self (which is a), the returned object is in fact A.i.

  • secondly, self.i + 1 then creates a new object whose value is the incremented value of A.i. But now this object is DIFFERENT from the object of name A.i: it has a different identity, that is to say a different localisation in the memory.

  • thirdly, there's an assignement: self.i = .....
    It means that the name i is created in the namespace of self (which is a) and binded to the just newly created object with incremented value.

So between the beginning of the add_i() function and its end, the meaning of self.i has changed.
Consequently, the meaning of a.i after the instruction a.add_i() is no more the same as before.

.

In-depth comprehension of Python processes requires to consider the games that identifiers (the names) and objects are playing in namespaces, there is no other way than that.

6 Comments

That is a very though in depth answer. Thank you for that. So you are saying that if self.i is not found on the instance then self.i resolved to A.i? Also self.i + 1 creates a new object why? Is it because i was not found on self?
Not me saying that: it is the doc. And the execution proves it. That's why my answer is long, and may appear verbose to some people, it is to bring the proffs. There's proof with the code, and proff with the documentation. - By the way, your question, as if you wasn't convinced, proves also that these processes need in-depth explanations. I thank you for your interest in my answer.
self.i + 1 creates a new object because its value is different from the value of self.i and integers are immutable objects. That means there's an object in the memory with value 0, then if a value 1 is needed, an object with this value is created to represent it.
What I call an object is not only the fact that it is an instance of a class (all is object in Python) but also the fact that an object is something real in the memory (the RAM): that's why I never understood why the doc says that "Objects are Python’s abstraction for data." here One must know that also: "Every object has an identity, a type and a value." , "An object’s identity never changes once it has been created;" , "An object’s type is also unchangeable" , .....
.... "The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable." But I don't fully understand this sentence because I don't know enough what happens under the hood concerning the way the mutable objects are built, I mean implemented, I mean made in existence in the RAM
|
2

Because you are calling the method on an instance of the class which has it's own copy of i. This code would do what you want it to do.

class A(object):
    i = 0
    def add_i(self):
        self.__class__.i += 1      

>> a = A()
>> a.add_i()
>> A.i
1

Comments

1

There exists copies of the variable i. When you call a.add_i(), a.i increments, while A.i becomes the same.

#a.i = 1, A.i = 1
a.add_i()
#a.i = 2, A.i = 2

Think about what would happen if you had

a = A()
b = A()
a.add_i()

print A.i()

1 Comment

"There exists copies of the variable i" That's a wrong way to express. variable is an ambiguous word in Python. copy is ambiguous too, there are true copies and false copies. In fact you mean that a has its own attribute with name a and that it increments. I show in my answer that it is wrong.
0

Because a.add_i() is not static method and it can increment only not static variables

Comments

0

In order to change class-level variable, you may define class-level method

In [15]: class A():
   ....:    i = 0
   ....:    @classmethod
   ....:    def incr(cls):
   ....:         cls.i += 1
   ....:         

In [16]: 

In [16]: A.incr()

In [17]: A.i
Out[17]: 1

In [18]: A().incr()

In [19]: A.i
Out[19]: 2

EDIT:

Or reference the class within method (and not instance)

def incr(self):
    A.i += 1

If the class-level attribute is mutable, and you want to change it (not assignment), e.g. for list, you may do it any way

In [29]: class Class4Test(object):
    mylist = []
    @classmethod
    def add2list_class(cls, val):
        cls.mylist.append(val)
    def add2list_by_class(self, val):
        Class4Test.mylist.append(val)
    def add2list_by_inst(self,val):
        self.mylist.append(val)
   ....:         

In [30]: obj = Class4Test()

In [31]: obj.add2list_by_inst('val1')

In [32]: obj.add2list_by_class('val2')

In [33]: Class4Test.add2list_class('val3')

In [34]: obj.add2list_class('val4')

In [35]: Class4Test.mylist
Out[35]: ['val1', 'val2', 'val3', 'val4']

3 Comments

no you don't, you simply need to be sure to access the class attribute rather than the instance attribute.
I took the -1 off, but there's still an issue here. In your first example, if you subclass A with another class B the class method will reference the subclass so you still end up hiding A.i behind B.i. This is the power of classmethod as it always references the actual class rather than the one in which it is defined, but it makes it unsuitable as a method of accessing attributes on a specific class.
Very good remark, Duncan. I wasn't aware of this potential issue. I'v just updated your answer with the more points in your user's page.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.