Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
307 views
in Technique[技术] by (71.8m points)

python - How does the function that is called inside the class declaration?

Have this code:

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

Obviously, the call of zope.interface.implements in some way alters the properties and behavior of the class Foo.

How does this happen? How do I use this approach in my code?

Example code is the part of zope.interface module.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The detailed "what happens"

The zope.interface.implements() function inspects the frame stack and alters the locals() namespace (a python dict) of the class-in-construction. Everything within a class statement in python is executed in that namespace, and the result forms the class body.

The function adds an extra value to the class namespace, __implements_advice_data__ with some data (the interfaces you've passed to the function, and the classImplements callable, something that'll be used later on.

It then either adds or chains in a metaclass for the class in question, by adding the (or altering a pre-existing) __metaclass__ key in the namespace. This ensures that in the future, every time you create an instance of the class, the metaclass now installed will be called first.

In fact, this metaclass (the class advisor) is a little devious; it removes itself again after the first time you create an instance. It simply calls the callback specified in the __implements_advice_data__ together with the interfaces you passed to the original implements() function, right after it either deletes the __metaclass__ key from the class, or replaces it with the original __metaclass__ (which it called to create the first class instance). The callback cleans up after itself, it removes the __implements_advice_data__ attribute from the class.

The short version

In summary, all the work zope.interface.implements() does is:

  • Add the passed interfaces, together with a callback to a special attribute in the class (__implements_advice_data__).
  • Ensures that the callback is called the first time you create an instance, using a special metaclass.

In the end, it's the moral equivalent of defining your interfaces like this:

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)

except that the last call is postponed until you first create an instance of Foo.

But why go to such lengths?

When zope.interface was first developed, Python did not yet have class decorators.

zope.interface.classImplements() needs to be called separately, as a function, after the class has been created, and a zope.interface.implements() call within the class body provides better documentation about what interfaces a class implements. You can place it right at the top of a class declaration, and everyone can see this important piece of information when looking at the class. Having a classImplements() call located after the class declaration is not nearly as visible and clear, and for long class definitions it is easily going to be missed altogether.

PEP 3129 finally did add class decorators to the language, and they were added to python 2.6 and 3.0; zope.interface was first developed back in the days of python 2.3 (IIRC).

Now that we do have class decorators, zope.interface.implements() has been deprecated, and you can use the zope.interface.implementer class decorator instead:

@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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...