Hooks, aspect-oriented programming, and design by contract

Alex Martelli aleax at aleax.it
Wed Jan 23 10:17:01 EST 2002


<montanaro at tttech.com> wrote in message
news:15438.50149.145878.29445 at dynamic2.tttech1.ttt...
    ...
> This confuses me a bit.  I assumed looking at your code that all that was
> necessary was that there be an object with the name "__metaclass__"
in-scope
> at class definition time.  I was surprised that this slight modification
of

That might make more sense, perhaps, but it's not defined that way.

> In PEP 253 Guido said:
>
>   - a class statement can contain a __metaclass__ declaration,
>     specifying the metaclass to be used to create the new class

Sure.

> Which lead me to this modification:
>
>     def makeaclassandafewinstances(meta=type):
>         print "using", meta
>         class justaclass:
>             __metaclass__ = meta
>             def __del__(self): print "destroying an instance"
>         a = justaclass()
>         b = justaclass()
>
> which does work.  This, however:

Yes, it satisfies the PEP's clause.


>     def makeaclassandafewinstances(__metaclass__=type):
>         print "using", __metaclass__
>         class justaclass:
>             __metaclass__ = __metaclass__
>             def __del__(self): print "destroying an instance"
>         a = justaclass()
>         b = justaclass()
>
> didn't, yielding a NameError for __metaclass__.
>
> Can you explain why your version works (__metaclass__ as a global), while
> the two versions with __metaclass__ as a local didn't and the version
using
> meta as a local did?

Because the PEP proceeds by explaining that, unless __metaclass__ is
defined in class scope, the next attempt is looking it
up in *global* scope -- it's never looked up in *local* scope.  It's
function build_class in Python\ceval.c that implements this exact
specification: after trying and failing to find __metaclass__ in the
class being defined, or using the type its LEFTMOST base, for a
baseless class not directly defining __metaclass__ it falls down to:

    PyObject *g = PyEval_GetGlobals();
    if (g != NULL && PyDict_Check(g))
        metaclass = PyDict_GetItemString(g, "__metaclass__");

i.e., globals-only, as per PEP.

I _think_ the idea is that this "last-ditch fallback" is to be
used only at module-scope -- perhaps even my approach, that of
_changing_ the global __metaclass__ dynamically, only works "by
accident", if the intention is not to have it dynamic at all.


Alex






More information about the Python-list mailing list