【问题标题】:How does the function that is called inside the class declaration?在类声明中调用的函数如何?
【发布时间】:2012-10-30 15:54:12
【问题描述】:

有这个代码:

>>> class Foo:
...     zope.interface.implements(IFoo)
...
...     def __init__(self, x=None):
...         self.x = x
...
...     def bar(self, q, r=None):
...         return q, r, self.x
...
...     def __repr__(self):
...         return "Foo(%s)" % self.x

显然,zope.interface.implements 的调用以某种方式改变了 Foo 类的属性和行为。

这是怎么发生的?如何在我的代码中使用这种方法?

示例代码是zope.interface模块的一部分。

【问题讨论】:

  • 值得注意的是,从我在the source 中看到的内容来看,这是一种已弃用的方法,现在首选的方法是@implementer 装饰器。

标签: python metaprogramming zope zope.interface


【解决方案1】:

详细的“发生了什么”

zope.interface.implements() 函数检查帧堆栈并更改构造中类的 locals() 命名空间(python dict)。 python 中class 语句中的所有内容都在该命名空间中执行,结果形成类体。

该函数为类命名空间添加了一个额外的值,__implements_advice_data__ 和一些数据(您已传递给函数的接口,以及 classImplements 可调用对象,稍后会用到。

然后,它通过在命名空间中添加(或更改预先存在的)__metaclass__ 键,为相关类添加或链接元类。这样可以确保以后每次创建类的实例时,都会首先调用现在安装的元类。

事实上,这个元类(类顾问)有点狡猾;在您第一次创建实例后,它会再次删除自身。它只是调用__implements_advice_data__ 中指定的回调以及您传递给原始implements() 函数的接口,之后它要么从类中删除__metaclass__ 键,要么将其替换为原始__metaclass__(其中它调用创建第一个类实例)。回调会自行清理,它会从类中删除 __implements_advice_data__ 属性。

短版

总而言之,zope.interface.implements() 所做的所有工作是:

  • 添加传递的接口,以及对类中特殊属性的回调 (__implements_advice_data__)。
  • 确保在您第一次使用特殊元类创建实例时调用回调。

归根结底,这样定义你的接口在道德上是等价的:

class Foo:
    def __init__(self, x=None):
        self.x = x

    def bar(self, q, r=None):
        return q, r, self.x

    def __repr__(self):
        return "Foo(%s)" % self.x

zope.interface.classImplements(Foo, IFoo)

除了最后一次调用被推迟到您第一次创建Foo 的实例之前。

但是为什么要这么费劲呢?

zope.interface 最初开发时,Python 还没有类装饰器。

zope.interface.classImplements() 需要在创建类之后作为函数单独调用,而在类主体中调用zope.interface.implements() 可以更好地说明类实现了哪些接口。您可以将它放在类声明的顶部,每个人在查看类时都可以看到这条重要信息。将classImplements() 调用定位在之后,类声明几乎没有那么明显和清晰,而且对于长类定义,很容易完全错过它。

PEP 3129 终于在语言中添加了类装饰器,它们被添加到了 python 2.6 和 3.0; zope.interface 最初是在 python 2.3 (IIRC) 时代开发的。

现在我们确实有了类装饰器,zope.interface.implements() 已被弃用,您可以改用zope.interface.implementer 类装饰器:

@zope.interface.implementer(IFoo)
class Foo:
    def __init__(self, x=None):
        self.x = x

    def bar(self, q, r=None):
        return q, r, self.x

    def __repr__(self):
        return "Foo(%s)" % self.x

【讨论】:

    【解决方案2】:

    Read the source, luke:

    http://svn.zope.org/zope.interface/trunk/src/zope/interface/declarations.py?rev=124816&view=markup

    def _implements(name, interfaces, classImplements):
        frame = sys._getframe(2)
        locals = frame.f_locals
    
        # Try to make sure we were called from a class def. In 2.2.0 we can't
        # check for __module__ since it doesn't seem to be added to the locals
        # until later on.
        if (locals is frame.f_globals) or (
            ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)):
            raise TypeError(name+" can be used only from a class definition.")
    
        if '__implements_advice_data__' in locals:
            raise TypeError(name+" can be used only once in a class definition.")
    
        locals['__implements_advice_data__'] = interfaces, classImplements
        addClassAdvisor(_implements_advice, depth=3)
    

    【讨论】:

    • 这只是zope.interface整个舞蹈的一小部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 2014-07-23
    • 2018-03-14
    • 1970-01-01
    • 2020-02-28
    相关资源
    最近更新 更多