给定一个方法,如何在Python 3.3中返回它所属的类?

Nei*_*l G 7 python metaprogramming python-3.3

给出x = C.f之后:

class C:
    def f(self):
        pass
Run Code Online (Sandbox Code Playgroud)

我会打电话给谁x回来C

我能做的最好的是exec解析部分x.__qualname__,这是丑陋的:

exec('d = ' + ".".join(x.__qualname__.split('.')[:-1]))
Run Code Online (Sandbox Code Playgroud)

对于一个用例,想象一下我想要一个装饰器来添加super对它应用的任何方法的调用.那个只给出函数对象的装饰器怎么能把类带到super(???下面)?

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
Run Code Online (Sandbox Code Playgroud)

Yoe*_*oel 5

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

x.__globals__[x.__qualname__.rsplit('.', 1)[0]]
Run Code Online (Sandbox Code Playgroud)

要么:

getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0])
Run Code Online (Sandbox Code Playgroud)

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

  • 来自:新增功能Python 3.3

    函数和类对象具有一个新__qualname__属性,表示从模块顶层到其定义的“路径”。对于全局函数和类,这与相同__name__。对于其他函数和类,它提供了有关它们的实际定义位置以及如何从全局范围访问它们的更好信息。

  • 来自__qualname__PEP 3155中的描述

    对于嵌套的类,方法和嵌套函数,该__qualname__属性包含指向模块顶层对象的点线路径。

编辑:

  1. 如@eryksun的评论所指出的那样,这样的解析__qualname__超出了其预期的用途,并且考虑到如何__qualname__反映闭包是极其脆弱的。一种更强大的方法需要排除形式的闭包命名空间name.<locals>。例如:

    >>> 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>'
    
    Run Code Online (Sandbox Code Playgroud)

    可以通过以下方式处理此特定情况:

    >>> getattr(inspect.getmodule(x),
    ...         x.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
    <class '__main__.C'>
    
    Run Code Online (Sandbox Code Playgroud)

    尽管如此,目前尚不清楚还有哪些其他极端情况或将来的版本中可能还会出现。

  2. 如@MichaelPetch的评论中所述,此答案仅与Python 3.3向前有关,因为只有__qualname__属性被引入语言时才如此。

    • 但是,根据@WouterBolsterlee的说法,github.com/ wbolster/ qualname提供了与旧Python版本等效的功能。
  3. 有关也可以处理绑定方法的完整解决方案,请参考此答案