4
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.

3
  • Why are you doing this? Could you compare self.__class__ instead? Could you use an Enum instead? Commented Mar 26, 2016 at 21:28
  • you could handle that using new like django does in its models / fields Commented Mar 26, 2016 at 21:34
  • 1
    I don't understand the downvotes you are getting. Your question is legitimate. Have an upvote from me. Commented Mar 26, 2016 at 21:50

4 Answers 4

4

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
Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

0
class MyClass(object):
    next_number = 0

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

class MyClassA(MyClass):
    pass

a = MyClassA()
print a.next_number
b = MyClassA()
print a.next_number, b.next_number
print a.number, b.number

output is

  1
  2, 2
  0, 1

Is this what you need?

2 Comments

The problem with this approach is that next_number is now attached to the MyClassA class. If you add another derived class MyClassB(MyClass) you will see that its next_number attribute follows an entirely separate sequence. I want next_number to increment for any new MyClass or subclass.
I didn't catch that, but @Blckknght seems to have
0
class Test(object):
    next_number = 0

    def __new__(cls, *args, **kwargs):
        cls.next_number += 1
        instance = super(Test, cls).__new__(cls)
        instance.next_number = cls.next_number
        return instance

Each Test() call method invoke class new method which as constructor return object instance to which we add field next_number(increased with each time of class invocation) which is then available in your class init method.

Comments

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.