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

时间:2014-09-18 20:06:38

标签: python metaprogramming python-3.3

给出x = C.f之后:

class C:
    def f(self):
        pass

我会在x上呼叫哪些会返回C

我能做的最好的事情是exec x.__qualname__的解析部分,这很难看:

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

对于一个用例,假设我想要一个装饰器,它会对它应用的任何方法添加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

2 个答案:

答案 0 :(得分:4)

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

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

或:

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

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

    来自What's new in Python 3.3
  •   

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

  • 来自__qualname__'s description in PEP 3155
  •   

    对于嵌套的分类,方法和嵌套函数,__qualname__属性包含从模块顶层通向对象的虚线路径。

编辑:

  1. 正如@eryksun的评论所述,解析__qualname__这样的内容超出了预期用途,考虑__qualname__反映closures的方式,它非常脆弱。更健壮的方法需要排除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>'
    

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

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

    尽管如此,目前尚不清楚现在存在哪些其他角落案件,或者未来版本中可能会出现这种情况。

  2. 正如@MichaelPetch的评论中所述,此答案仅适用于Python 3.3以及the __qualname__ attribute was introduced之后的语言。

  3. 有关处理绑定方法的完整解决方案,请参阅this answer

答案 1 :(得分:1)

我将贡献一个选项,它依赖于 gc 模块向后跟踪引用。

它依赖于当然不能保证的实现细节,并且肯定不会适用于所有 Python 实现。尽管如此,某些应用程序可能会发现此选项比使用 MFMessageComposeViewController 更可取。

你实际上需要向后两跳,因为类在其中隐藏了一个字典,它保存了成员函数:

__qualname__