Getting name of subclass from superclass?

12,783

Solution 1

Just as an alternative to @poorsod's solution, which works perfectly fine for me, here's another, perhaps simpler variant without class methods, using self.__class__. Just as self always points to the current instance, self.__class__ always points to the actual class of that instance.

class bc(object):
    def get_subclass_name(self):
        return self.__class__.__name__

Example, tested in Python 2.6.6:

>>> class sc1(bc): pass
>>> class sc2(sc1): pass
>>> class sc3(sc2): pass
>>> print sc2().get_subclass_name()
sc2

If this does not work, please be more specific as to what output you expect and what output you are getting instead.

Solution 2

You need a class method.

class bc(Object):
    @classmethod
    def get_subclass_name(cls):
        return cls.__name__

    def somemethod(self):
        x = self.get_subclass_name()

Normal methods, when invoked, get passed the instance as the first parameter. This is why you see self everywhere in Python.

When you invoke a class method, however, the concrete class of the instance is passed to the function. We usually use the name cls, as I have done here.

The classic use case for class methods is alternative constructors. For example, the standard library's dict class provides a fromkeys class method, which constructs a new dict from the supplied keys and a single value. You could construct such a dict manually, but the Python developers made life easy for you!

Share:
12,783
Jonathan
Author by

Jonathan

I am a retired software designer and am currently working on aspects of computer recognition of natural language using Python.

Updated on July 23, 2022

Comments

  • Jonathan
    Jonathan almost 2 years

    I have a base class bc and a number of subclasses based on bc.

    class bc(Object):
      def get_subclass_name(self):
          # Retrieve the name of the most derived subclass of this instance
          pass
      def somemethod(self):
          x = self.get_subclass_name()
    
    class sc1(bc):
        pass
    
    class sc2(bc)
        pass
    

    The idea is that when somemethod() is invoked on an instance of a subclass of bc, it will be able to use the name of the most derived subclass of that instance without needing to know in advance what potential subclasses may exist.

    I have put together a test case for this:

    class base_class(object):
        @classmethod
        def get_subclass_name(cls):
            return cls.__name__
    
        def somemethod(self):
            print(base_class.get_subclass_name())
    
    class sub_class(base_class):
        pass
    
    sub_class().somemethod()
    

    When this code is run it produces base_class rather than sub_class.

  • Jonathan
    Jonathan over 10 years
    If sc is derived from bc, you will get bc as the name, not sc as I want. I have already tried this.
  • tobias_k
    tobias_k over 10 years
    Why not just use self.__class__?
  • Benjamin Hodgson
    Benjamin Hodgson over 10 years
    @Jonathan Have I misunderstood your requirements? If you call get_subclass_name on an instance of sc, it'll return 'sc'.
  • Benjamin Hodgson
    Benjamin Hodgson over 10 years
    @tobias_k Yes, this is roughly equivalent to using self.__class__, but it's much more Pythonic (it's both less verbose and more explicit).
  • Jonathan
    Jonathan over 10 years
    I was looking for sc3 in this instance.
  • tobias_k
    tobias_k over 10 years
    Please explain why. sc2 is the actual class of the instance, as an instance of sc2 is created: sc2().get_.... sc3 is there just for avoiding the ambiguity of "most derived class".
  • Jonathan
    Jonathan over 10 years
    Many thanks. Your proposal worked just fine, but the proposal using classmethod from @poorsod did not when I would have expected both to yield the same result.
  • tobias_k
    tobias_k over 10 years
    @Jonathan The answer using classmethods works, too. According to your edit you are just using it the wrong way. See my comment below your question above.
  • Anubis
    Anubis almost 10 years
    @tobias_k in this approach you need to create an instance first, is there any way to get the sub class name (exact requirement in the question), but without creating an instance??
  • tobias_k
    tobias_k almost 10 years
    @Anubis What do you mean? You have a class c and want to get it's name? Just do c.__name__? Or do you want the name of some subclass of c? If so, which subclass to take (there could be many) without providing an instance of that subclass?
  • Anubis
    Anubis almost 10 years
    @tobias_k can you please check my question here: stackoverflow.com/q/24953216/1547699