Przemek D
Przemek D

Reputation: 664

Inheriting a virtual class method - how to call it from base class?

Let B inherit from A. Suppose that some of B's behavior depends on the class attribute cls_x and we want to set up this dependency during construction of B objects. Since it is not a simple operation, we want to wrap it in a class method, which the constructor will call. Example:

class B(A):
  cls_x = 'B'

  @classmethod
  def cm(cls):
    return cls.cls_x

  def __init__(self):
    self.attr = B.cm()

Problem: cm as well as __init__ will always be doing the same things and their behavior must stay the same in each derived class. Thus, we would like to put them both in the base class and not define it in any of the derived classes. The only difference will be the caller of cm - either A or B (or any of B1, B2, each inheriting from A), whatever is being constructed. So what we'd like to have is something like this:

class A:
  cls_x = 'A'

  @classmethod
  def cm(cls):
    return cls.cls_x

  def __init__(self):
    self.attr = ClassOfWhateverIsInstantiated.cm()  #how to do this?

class B(A):
  cls_x = 'B'

I feel like it's either something very simple I'm missing about Python's inheritance mechanics or the whole issue should be handled entirely differently.

This is different than this question as I do not want to override the class method, but move its implementation to the base class entirely.

Upvotes: 1

Views: 395

Answers (2)

Richard Inglis
Richard Inglis

Reputation: 5958

For ClassOfWhateverIsInstantiated you can just use self:

class A:
  cls_x = 'A'

  @classmethod
  def cm(cls):
    return cls.cls_x

  def __init__(self):
    self.attr = self.cm()  # 'self' refers to B, if called from B

class B(A):
  cls_x = 'B'

a = A()
print(a.cls_x) # = 'A'
print(A.cls_x) # = 'A'

b = B()
print(b.cls_x) # = 'B'
print(B.cls_x) # = 'B'

To understand this, just remember that class B is inheriting the methods of class A. So when __init__() is called during B's instantiation, it's called in the context of class B, to which self refers.

Upvotes: 1

Aran-Fey
Aran-Fey

Reputation: 43276

Look at it this way: Your question is essentially "How do I get the class of an instance?". The answer to that question is to use the type function:

ClassOfWhateverIsInstantiated = type(self)

But you don't even need to do that, because classmethods can be called directly through an instance:

def __init__(self):
    self.attr = self.cm()  # just use `self`

This works because classmethods automatically look up the class of the instance for you. From the docs:

[A classmethod] can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class.

Upvotes: 1

Related Questions