Given A Method, How Do I Return The Class It Belongs To In Python 3.3 Onward?
Solution 1:
If your aim is to get rid of the exec
statement, but are willing to use the __qualname__
attribute, even though you are still required to manually parse it, then at least for simple cases the following seems to work:
x.__globals__[x.__qualname__.rsplit('.', 1)[0]]
or:
getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0])
I'm not a Python
expert, but I think the second solution is better, considering the following documentation excerpts:
from What's new in
Python 3.3
:Functions and class objects have a new
__qualname__
attribute representing the “path” from the module top-level to their definition. For global functions and classes, this is the same as__name__
. For other functions and classes, it provides better information about where they were actually defined, and how they might be accessible from the global scope.from
__qualname__
's description in PEP 3155:For nested classed, methods, and nested functions, the
__qualname__
attribute contains a dotted path leading to the object from the module top-level.
EDIT:
As noted in the comments by @eryksun, parsing
__qualname__
like this goes beyond its intended usage and is extremely fragile considering how__qualname__
reflects closures. A more robust approach needs to exclude closure namespaces of the formname.<locals>
. For example:>>> classC: ... f = (lambda x: lambda s: x)(1) ... >>> x = C.f >>> x <function C.<lambda>.<locals>.<lambda> at 0x7f13b58df730> >>> x.__qualname__ 'C.<lambda>.<locals>.<lambda>'>>> getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0]) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'module'object has no attribute 'C.<lambda>.<locals>'
This specific case can be handled in the following manner:
>>> getattr(inspect.getmodule(x), ... x.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0]) <class'__main__.C'>
Nonetheless, it's unclear what other corner cases exist now or may come up in future releases.
As noted in the comment by @MichaelPetch, this answer is relevant only for
Python 3.3
onward, as only then the__qualname__
attribute was introduced into the language.- However, according to @WouterBolsterlee, github.com/wbolster/qualname provides an equivalent for older
Python
versions.
- However, according to @WouterBolsterlee, github.com/wbolster/qualname provides an equivalent for older
For a complete solution that handles bound methods as well, please refer to this answer.
Solution 2:
I'll contribute one more option that relies on the gc module to follow references backwards.
It relies on implementation details that certainly aren't guaranteed and certainly won't work on all Python implementations. Nevertheless, some applications may find this option preferable to working with __qualname__
.
You actually need two hops backwards, because the class hides a dict inside it, which holds the member function:
defclass_holding(fn):
'''
>>> class Foo:
... def bar(self):
... return 1
>>> class_holding(Foo.bar)
<class Foo>
'''for possible_dict in gc.get_referrers(fn):
ifnotisinstance(possible_dict, dict):
continuefor possible_classin gc.get_referrers(possible_dict):
ifgetattr(possible_class, fn.__name__, None) is fn:
return possible_classreturnNone
Post a Comment for "Given A Method, How Do I Return The Class It Belongs To In Python 3.3 Onward?"