Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python class variable update

Tags:

python

oop

class MyClass(object):
    next_number = 0

    def __init__(self):
        self.number = self.next_number
        MyClass.next_number += 1

class MyClassA(MyClass):
    pass

Above it would appear I am forced hardcode the class name when updating the class variable on line 6 since writing:

self.__class__.next_number += 1

results in a new class variable in any derived class. Is there any alternative to hardcoding the class name?

Also on line 5 should I instead write:

self.number = self.__class__.next_number

to make it obvious next_number is a class variable.

like image 296
user3235250 Avatar asked Oct 19 '25 11:10

user3235250


2 Answers

One way to dodge around the issue of rebinding the class variable is to replace the immutable integer value with an mutable object that contains the number inside it. You can then mutate the object in place.

A simple version of this is to put the number in a one-element list:

class MyClass(object):
    next_number = [0]

    def __init__(self):
        self.number = self.next_number[0]
        self.next_number[0] += 1

That works, but it's not very elegant.

A much better approach would be to replace the integer class variable with an iterator returned by the itertools.count function, which will yield successively higher integers each time you call next on it (forever, it's an infinite iterator). At a high level this still works like the list code, since next mutates the iterator, but the exact details are hidden behind the iterator protocol (and the implementation of itertools.count).

class MyClass(object):
    number_iter = itertools.count()

    def __init__(self):
        self.number = next(self.number_iter) # this mutates number_iter by consuming a value

If you needed something a little more complicated, such that itertools didn't provide exactly the right sequence, you could write your own generator function and assign its return value to your class variable. For instance, here's a generator for the Fibonacci sequence:

def fib():
    a, b = 0, 1
    while True:
        a, b = b, a+b
        yield a
like image 95
Blckknght Avatar answered Oct 21 '25 12:10

Blckknght


You are correct that self.__class__.next_number would create a variable for each class that instantiate an object that calls this __init__.

You have 2 choices: hard code the class name as you did, or do not call super().__init__ from the derived class. I would go with the former.

In my opinion, you are unnecessarily worried about hard coding the class name here. After all, the class name is "hard coded" in this module already anyway when you do class MyClass(object):.

Regarding your second comment, it is a good consideration to have. I would go with: self.__class__.next_number += 1. Not only is this more explicit to the reader, but it also guarantees that you keep the appropriate behaviour, should you mistakenly define self.next_number later on.

like image 24
DevShark Avatar answered Oct 21 '25 11:10

DevShark