3

有这个代码:

>>> 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模块的一部分。

4

2 回答 2

6

详细的“发生了什么”

zope.interface.implements()函数检查帧堆栈并更改构造中的类的locals()名称空间(python )。python 语句中的dict所有内容都在该命名空间中执行,结果形成类主体。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
于 2012-11-11T11:23:05.263 回答
2

阅读源代码,卢克

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)
于 2012-11-10T15:17:35.483 回答