问题
Given x = C.f after:
class C:
def f(self):
pass
What do I call on x that will return C?
The best I could do is execing a parsed portion of x.__qualname__, which is ugly:
exec('d = ' + ".".join(x.__qualname__.split('.')[:-1]))
For a use case, imagine that I want a decorator that adds a super call to any method it's applied to. How can that decorator, which is only given the function object, get the class to super (the ??? below)?
def ensure_finished(iterator):
try:
next(iterator)
except StopIteration:
return
else:
raise RuntimeError
def derived_generator(method):
def new_method(self, *args, **kwargs):
x = method(self, *args, **kwargs)
y = getattr(super(???, self), method.__name__)\
(*args, **kwargs)
for a, b in zip(x, y):
assert a is None and b is None
yield
ensure_finished(x)
ensure_finished(y)
return new_method
回答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:>>> class C: ... 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.3onward, as only then the __qualname__ attribute was introduced into the language.- However, according to @WouterBolsterlee, github.com/wbolster/qualname provides an equivalent for older
Pythonversions.
- 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.
来源:https://stackoverflow.com/questions/25921450/given-a-method-how-do-i-return-the-class-it-belongs-to-in-python-3-3-onward