PEP 318 (was Re: [Python-Dev] Re: Guido's Magic Code was: inlinesort option)

Alex Martelli aleaxit at yahoo.com
Tue Nov 4 03:56:23 EST 2003


On Tuesday 04 November 2003 01:02 am, Delaney, Timothy C (Timothy) wrote:
> > From: Alex Martelli [mailto:aleaxit at yahoo.com]
> >
> > BTW, when we do come around to PEP 318, I would suggest the 'as'
> > clause on a class statement as the best way to specify a metaclass.
>
> I just realised what has been bugging me about the idea of
>
>     def foop() as staticmethod:
>
> and it applies equally well to
>
>     class Newstyle as type:
>
> Basically, it completely changes the semantics associated with 'as' in
> Python - which are to give something a different name (technically, to
> rebind the object to a different name).

Yes, that's what the 'as' clause means in from and import statements,
of course.


> OTOH, the first case above means 'create this (function) object, call this
> decorator, and bind the name to the new object'. So instead of taking an
> existing object (with an existing name) and rebinding it to a new name, it
> is creating an object, doing something to it and binding it to a name. A
> definite deviation from the current 'as' semantics, but understandable.

I'm not sure I follow.  "import X as y" means basically
    y = __import__('X')
(give or take a little:-).  'def foo() as staticmethod:' would mean instead
    foo = staticmethod(new.function(<codeobj>, globals(), 'foo'))
so what comes after the 'as' is a name to bind in the existing case, it's
a callable to call in the new proposed syntax.

There is a binding in each case, and in each case something is called
to obtain the object to bind; I think the distinction between new and
existing object is spurious -- __import__ can perfectly well be creating
a new object -- but the real distinction is that the name to bind is given
after 'as' in the existing case, it's NOT so given in the new proposed one.

> However, the second case above is doing something completely different. It

Not at all -- it does:
    Newstyle = type('Newstyle', (), <classdict>)
where <classdict> is built from the body of the 'class' statement, just
like, above, <codeobj> is built from the body of the 'def' statement.

I find this rather close to the 'as staticmethod' case: that one calls
staticmethod (the callable after the 'as') and binds the result to the
name before the 'as', this one calls type (the callable after the 'as')
and binds the result to the name before the 'as'.

> is creating a new object (a class) and binding it to a name. As a side
> effect, it is changing the metaclass of the object. The 'as' in this case

"changing"?  From what?  It's _establishing_ the type of the name it's
binding, just as (e.g.) staticmethod(...) is.  I.e., stripping the syntax we
have in today's Python:

>>> xx = type('xx', (), {'ba':23})
>>> type(xx)
<type 'type'>
>>> xx = staticmethod(lambda ba: 23)
>>> type(xx)
<type 'staticmethod'>

...so where's the "completely different" or the "changing" in one case
and not the other...?

> has nothing whatsoever to do with binding the object name, but a name in
> the object's namespace.

It has everything to do with determining the type of the object, just
like e.g. staticmethod would.

> I suppose you could make the argument that the metaclass has to act as a
> decorator (like in the function def above) and set the __metaclass__
> attribute, but that would mean that existing metaclasses couldn't work. It
> would also mean you were defining the semantics at an implementation level.

I'm sure I've lost you completely here, sorry.

>>> class xx(object): pass
...
>>> xx.__metaclass__
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: type object 'xx' has no attribute '__metaclass__'

why would a class created this way have to set '__metaclass__', again?

A metaclass is the class object's type, and it's called to create the class
object.  If I do "xx = type('xx', (), {})" I get exactly the same result as
with the above "class xx" statement -- no more, no less.  "class" just
gives me neat syntax to determine the 3 arguments with which the
metaclass is called -- a string that's the classname, a tuple of bases,
and a dictionary.

That "__metaclass__ attribute" is just an optional hack which Python
can decide to determine _which_ metaclass to call (in alternative to
others, even today) for a certain 'class' statement.


> I'm worried that I'm being too picky here, because I *like* the way the
> above reads. I'm just worried about overloading 'as' with too many
> essentially unrelated meanings.

I accept that in both 'def foo() as X' and 'class foo as X' the X in "as X"
is very different from its role in 'import foo as X' -- in the import
statement, X is just a name to which to bind an object, while in the
def and class statements X would be a callable to call in order to
get the object -- and the name to bind would be the one right after
the def or class keywords instead.  So maybe we should do as
Phillip Eby suggests and use 'is' instead - that's slightly stretched
too, because after "def foo() is staticmethod:" it would NOT be
the case that 'foo is staticmethod' holds, but, rather, that
isinstance(foo, staticmethod) [so we're saying "IS-A", not really "IS"].

But the def and class statements cases are SO close -- in both what
comes after the 'is' (or 'as') is a callable anyway.  The debate is
then just, should said callable be called with an already prepared
(function or class) object, just to decorate it; or should it rather be
called with the elementary "bricks" needed to build the object, so
it can build it properly.  Incidentally, it seems to me that it might not
be a problem to overload e.g. staticmethod so it can be called with
multiple arguments (same as new.function) and internally calls
new.function itself, should there be any need for that (not that I
can see any use case right now, just musing...).


Alex




More information about the Python-Dev mailing list