Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will a class variable in python be declared again when a new instance is created?

I'm practicing the following code:

class MITPerson(Person): # a subclass of class Person

    nextIdNum = 0 # identification number

    def __init__(self, name):
        Person.__init__(self, name)
        self.idNum = MITPerson.nextIdNum 
        MITPerson.nextIdNum += 1 

    def getIdNum(self):
        return self.idNum

For a reference, here is the superclass:

class Person(object):  # superclass

    def __init__(self, name):
        """Create a person"""
        self.name = name

I thought that I've already known the answer of this question since I try the following instances:

p1 = MITPerson('Mark Guttag')
p2 = MITPerson('Billy Bob Beaver')
p3 = MITPerson('Billy Bob Beaver')

Not surprisingly, when I type these into console:

In[12]: p1.getIdNum()

Out[12]: 0

In[13]: p3.getIdNum()

Out[13]: 2

I've read this post and checked all the excellent answers here: Static class variables in Python

I saw that nextIdNum is assigned to 0 when the first instance p1 is created. What I feel weird is that why not p2 and p3 also bind nextIdNum to 0 again? In my imagination, the class variable should be reassigned to 0 once a class MITPerson is created without calling the method.

Did I miss something?

By the way, I've also go through the tutorial here: https://docs.python.org/3.6/tutorial/classes.html#class-objects

However, I'm afraid it does not give out the answer :(

like image 230
cindy50633 Avatar asked Sep 29 '26 09:09

cindy50633


2 Answers

I saw that nextIdNum is assigned to 0 when the first instance p1 is created.

This is wrong. nextIdNum is assigned to 0 when the class is defined.

like image 92
Daniel Avatar answered Oct 02 '26 00:10

Daniel


A class statement is a kind of wrapper around a single call to type. The statements in the body are evaluated, then added to a dict that is passed to type.

Yours is roughly equivalent to

def _init_(self, name):
    Person.__init__(self, name)
    self.idNum = MITPerson.nextIdNum
    MITPerson.nextIDNum += 1

def _getIdNum(self):
    return self.idNum

MITPerson = type(
    'MITPerson',
    (Person,),
    {
        '__init__': _init,
        'getIdNum': _getIdNum,
        'nextIdNum': 0
    }
)

del _init, _getIdNum

You can see that nextIdNum gets initialized to 0 immediately, before any instances of MITPerson are created, just like __init__ and getIdNum.


When you create an instance, the following steps occur:

  1. MITPerson('Mark Guttag') resolves to type.__call__(MITPerson, 'Mark Guttag').
  2. __call__ invokes MITPerson.__new__('Mark Guttag'), which creates a new instance of MITPerson.
  3. The new value is passed to MITPerson.__init__, at which point the current value of MITPerson.nextIdNum is used before incrementing the class variable.
  4. Once __init__ returns, __new__ returns that value, at which point it is assigned to p1.

Notice none of the code in the body of the class statement is executed again, although the function __init__ defined there is.

like image 36
chepner Avatar answered Oct 01 '26 22:10

chepner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!