There are a few different issues here.
- 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)
__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'>
- 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())