7

I would expect the following code to print 012345 but it prints 012012. Why? I would expect the calls to incr to be accessing the same variables since they are inherited from the same class but they are clearly different variables.

class a(object):
    var = 0
    @classmethod
    def incr(cls):
        print cls.var
        cls.var+=1

class b(a):
    def func(self):
        super(b,self).incr()

class c(a):
    def func(self):
        super(c,self).incr()


t = a()
t1 = b()
t2 = c()
t1.func()
t1.func()
t1.func()
t2.func()
t2.func()
t2.func()
2
  • 1
    It doesn't effect the unexpected behavior you're seeing, but I want to note that you don't need to be using super here in either of your subclasses. You can just call self.incr() instead. You only need to use super when you want to skip over an different version of the function (usually because you've overridden in in the current class). Commented Aug 26, 2017 at 18:23
  • You should switch to Python 3.6. pythonclock.org Commented Aug 26, 2017 at 20:54

3 Answers 3

7

They are inherited from the same class, but the cls passed to the classmethod via super is the current class where the method was called from. super accesses the base class version of the method, but the cls for the call is the class where the super call was made.

This is one of the subtle differences between doing:

def func(self):
    super(c, self).incr() # same as a.__dict__['incr'].__get__(self, type(self))()

and:

def func(self):
    a.incr()

You can confirm this by printing the current cls in your incr method in both cases:

def incr(cls):
    print cls
    ...

You should never assume that all super does is make a method call bound to the parent class. It does a lot more.

Keep in mind that when the first augmented assignment += is performed, the initial value of var is read from the base class (since at this point it does not exist in the dict of the subclasses). The updated value is however written to the subclass. Calling super from the second subclass repeats the same behavior of reading the initial var value from a.

Sign up to request clarification or add additional context in comments.

2 Comments

You may want to be explicit about the fact that it's the cls.var+=1 line that behaves oddly when cls is different. The first time it's run, it reads the inherited value of var (which is 0 in the example, but could have been some other value if t.incr() had been called), but it writes to the specific cls rather than to the base class. All subsequent calls use the subclass's value without ever seeing the base class.
@Blckknght I'll add a short note about that.
5

There is a way to produce the sequence 012345. You have to make sure that the var of class a is increased in the incr method, even when it is called in the subclasses. To achieve this, increment by a.var += 1, not by cls.var += 1.

As pointed out by the other answers, the var is also inherited to b and c. By using cls.var += 1 both subclasses increase their own var instead of a's var.

class a:
    var = 0
    @classmethod
    def incr(cls):
        print(cls.var)
        a.var += 1

class b(a):
    def f(self):
        super().incr()

class c(a):
    def f(self):
        super().incr()

cb = b()
cc = c()
cb.incr()
cb.incr()
cb.incr()
cc.incr()
cc.incr()
cc.incr()

cb.incr()
cc.incr()

Produces:

0
1
2
3
4
5
6
7

Comments

0

Both class b and class c inherit from class a separately, and var is set to 0 each time.

One way to have class c to get the same value of var in class a as class b does, class c can inherit from class b like so:

class a(object):
    var = 0  
    @classmethod
    def incr(cls):
        print cls.var
        cls.var+=1

class b(a):
    def func(self):
        super(b,self).incr()

class c(b):
    def func(self):
        super(c,self).incr()


t = a()
t1 = b()
t2 = c()
t1.func()
t1.func() 
t1.func()
t2.func()
t2.func()
t2.func()` 

1 Comment

That only gives the expected result if the first call to t2.func() comes after all the t1.func() calls. If you mixed them up, you wouldn't get the result you'd expect. And it's very misleading to say that "var is set to 0 each time". There's only one var when it's zero (the one in a). It's after the first incr call that whatever class it was made on gets a new var attribute with the value 1.

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.