3

The output of:

class Dog():
    def get_class():
        return __class__

class Cat():
    def get_class():
        return __class__

print(Dog.get_class())
print(Cat.get_class())

is:

<class '__main__.Dog'>
<class '__main__.Cat'>

I want to DRY up my code with a subclass. But the output of:

class BaseClass():
    def get_class():
        return __class__

class Dog(BaseClass):
    pass

class Cat(BaseClass):
    pass

print(Dog.get_class())
print(Cat.get_class())

is

<class '__main__.BaseClass'>
<class '__main__.BaseClass'>

How do I change the code in the second case to obtain the same output as the first case?

Bradley Marques
  • 502
  • 3
  • 22
  • Code don't work. Either the method is static, either is takes a parameter. That code is incorrect – azro Jun 03 '22 at 06:09
  • Did you try `print(Dog)` and `print(Cat)`? – Karl Knechtel Jun 03 '22 at 06:09
  • @azro I can understand why you'd think so, but it actually does work in Python, to a limited extent. The `@staticmethod` decorator enables methods intended to be static to be looked up from an instance, but it's not necessary if the function (not a method!) will *only* be looked up from the class. In this case the class is simply acting as a namespace. – Karl Knechtel Jun 03 '22 at 06:11

2 Answers2

1

you are almost there :

class BaseClass:
    @classmethod
    def get_class(cls):
        return cls

class Dog(BaseClass):
    pass


class Cat(BaseClass):
    pass

print(Dog.get_class())
print(Cat.get_class())

<class '__main__.Dog'>
<class '__main__.Cat'>
baskettaz
  • 741
  • 3
  • 12
1

There are a few different issues here.

  1. The logic we are implementing is simply "get the class". If you want to do this starting specifically and only from the class, then there is nothing to do, and no reason to implement anything inside the BaseClass or Dog or Cat to get that result - because you already have it.
class BaseClass:
    pass

class Dog(BaseClass):
    pass

class Cat(BaseClass):
    pass

print(Dog)
print(Cat)
  1. __class__ is a special local variable that is used for the implementation of super(). It names the class where the method is defined, regardless of how that method was looked up, or even if it was used as a plain function:
>>> class x:
...   def example(self):
...     print(__class__)
... 
>>> class y(x): pass
... 
>>> x().example()
<class '__main__.x'>
>>> y().example()
<class '__main__.x'>
>>> x.example(42)
<class '__main__.x'>
  1. Normally, a method that does not expect to receive an instance of the class should be decorated with either @classmethod or @staticmethod. This way, the code can still be used with either a class or an instance.

The rules are: @classmethod - called with a class, the first argument is that class itself; called with an instance, the first argument is the instance's class. The parameters should include one at the start to receive that argument. By convention, we call that parameter cls.

@staticmethod - called with either a class or an instance, no argument is added for the call. The parameters should only list what will be explicitly passed.

No decorator - called with a class, no argument is added; called with an instance, the instance is added. This should be used only for instances, thus there should be a parameter to receive the instance argument. By convention, we call that parameter self.

Trying to use a function inside a class without either a decorator or self violates the standard expectations. It tries to treat the class as simply a namespace. This is not what they are for, even thought it sort of works.


Supposing that we want the code to work with either a class (giving us back that class) or an instance (giving us back that instance's class), the code is trivial: a @classmethod-decorated method already receives a parameter that is exactly what we want, in every case, so we simply return that. Thus:

class BaseClass:
    @classmethod
    def get_class(cls):
        return cls

class Dog(BaseClass):
    pass

class Cat(BaseClass):
    pass

print(Dog.get_class())
print(Dog().get_class())
print(Cat.get_class())
print(Cat().get_class())
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • Thanks for the detailed response. In reality, I was trying to do more than get the class's name, and this was just a silly example that I though illustrated a minimum working example. In this case, @classmethod was the decorator I needed. – Bradley Marques Jun 03 '22 at 07:21