I've encountered situations where standard methods like __module__
and __class__
become unreliable due to inheritance hierarchies or metaclass-based class creation. I need a robust approach that can accurately identify the module and class, regardless of the complexity of the class structure.
Here's an example of a potential issue:
class BaseClass:
@classmethod
def mymethod(cls):
print(cls.__module__, cls.__name__)
class DerivedClass(BaseClass):
pass
DerivedClass.mymethod()
This will output __main__ DerivedClass
, which might not be the desired result if you need to differentiate between the base class and derived class methods.
I'm looking for a solution that can handle scenarios like these and provide accurate module and class information, even in the presence of inheritance, metaclasses, and other advanced Python constructs.
To get the module and class of the defining type, you can look at the qualified name. With this approach, the derived class could be defined in a separate module entirely.
import logging
class BaseClass:
@classmethod
def mymethod(cls):
_, _, methodname, _ = logging.getLogger().findCaller()
func = getattr(cls, methodname)
print("module:", func.__module__)
print("method name:", methodname)
print("qualname:", func.__qualname__)
print("defining class:", func.__qualname__.split(".")[0])
class DerivedClass(BaseClass):
pass
DerivedClass.mymethod()
Note that direct invocations of findCaller()
were bugged until Python 3.11.
A somewhat deviant approach which still works in older Python versions:
class BaseClass:
@classmethod
def mymethod(cls):
methodname = (lambda:0).__qualname__.split(".")[1]
func = getattr(cls, methodname)
print("module:", func.__module__)
print("method name:", methodname)
print("qualname:", func.__qualname__)
print("defining class:", func.__qualname__.split(".")[0])
class DerivedClass(BaseClass):
pass
DerivedClass.mymethod()