关于元编程:给定一个方法,如何在Python 3.3中返回它所属的类?

Given a method, how do I return the class it belongs to in Python 3.3 onward?

x = C.f后:

1
2
3
class C:
    def f(self):
        pass

怎么在一个呼叫xC回报?

最好的我可以做的是一份x.__qualname__execing parsed学院,这是丑陋的。

1
exec('d = ' +".".join(x.__qualname__.split('.')[:-1]))

一个用例,想象我想要的那一类super鱼鳞,调用任何方法,它的应用。这是如何装饰,只把给定的函数对象类,一个super(???下面)?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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


如果您的目标是摆脱exec语句,但愿意使用__qualname__属性,即使您仍然需要手动解析它,那么至少在简单的情况下,以下似乎可以工作:

1
x.__globals__[x.__qualname__.rsplit('.', 1)[0]]

或:

1
getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0])

我不是Python专家,但考虑到以下文档摘录,我认为第二个解决方案更好:

  • 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.

  • 根据__qualname__在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.

编辑:

  • 正如@eryksun在评论中所指出的,像这样分析EDOCX1[1]超出了它的预期用途,考虑到EDOCX1[1]如何反映闭包,它非常脆弱。更健壮的方法需要排除形式为name.的闭包名称空间。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    >>> 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>'

    具体情况可按以下方式处理:

    1
    2
    3
    >>> getattr(inspect.getmodule(x),
    ...         x.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
    <class '__main__.C'>

    尽管如此,目前还不清楚在未来的版本中还会出现什么其他的角落案例。

  • 正如@michaelpetch在评论中所指出的,这个答案只与Python 3.3之后的版本相关,因为只有在那时__qualname__属性才被引入到语言中。

    • 然而,根据@wouterbolsterlee的说法,github.com/wbolster/qualname提供了与旧版Python版本相同的版本。
  • 有关处理绑定方法的完整解决方案,请参阅此答案。