[Python-3000] defop ?

Phillip J. Eby pje at telecommunity.com
Wed Nov 22 21:52:21 CET 2006


At 11:52 AM 11/22/2006 -0800, Guido van Rossum wrote:
>Do I understand correctly that the syntax is more like
>
>   'defop' <expr> '(' <args> ')' ':' <suite>
>
>than like
>
>   'defop' NAME '(' <args> ')' ':' <suite>
>
>? (That would need some kind of limitation on the syntax for <expr> so
>that it can't contain a call, or else it would be syntactically
>ambiguous.)

Yes - it should be the same as for decorators or imports, i.e. a possibly 
dotted name.


>What kind of object should the expression produce? What methods on it
>are we going to call, with what arguments?

The defop statement (and its decorators if any) would translate to this code:

     @<decorators>
     def <anonfunc>(<args>):
         <suite>

     addmethod(<expr>, <anonfunc>, enclosing_class_or_None)

In other words, addmethod() is invoked following completion of the class 
suite (if there is one), and passed the decorated anonymous function object.

Now, we could say that addmethod() just calls expr.__addmethod__.  This 
would work as long as each builtin had its own __addmethod__() that did the 
'cls.__special__ = method' work.

However, I personally find it easier to just think of addmethod() as a 
generic function, so that you can define methods for it that recognize the 
builtin generics and do the right thing.  This also doesn't require the 
builtins to grow new methods, and thus can be implemented in today's Python 
using only Python code.  (Like the example that you described as 
brain-exploding orange smoke, which was an actual implementation of 
addmethod that would work in today's Python 2.x)

But if it's easier for you to conceptualize, let's just say it expects expr 
to have an __addmethod__ method.


>  I'd love to see an
>explanation that showed how instead of defop we could also use a
>certain decorator. Perhaps
>
>   defop <expr> ( <args> ) : <suite>
>
>is equivalent to
>
>   @defop(<expr>)
>   def _ ( <args> ) : <suite>  # The method name is immaterial here
>
>? Then what goes into the defop decorator? And how does this jive with
>it being inside a class? (And what would it do if it wasn't?)

An implementation for today's Python:

     from peak.util.decorators import decorate_class

     def defop(expr):
         def decorate(func):
             def do_add(cls):
                 addmethod(expr, func, cls)
                 return cls
             try:
                 decorate_class(do_add)
             except SyntaxError:   # not in a class
                 do_add(None)      # handle top-level function
         return decorate

(The 'decorate_class' function applies its argument as a class decorator 
for the class its caller is invoked from.  This is the same mechanism (and 
actual code!) that Zope uses to do in-body class decorators like 
'implements()'.)  decorate_class raises a SyntaxError if its caller is not 
being run directly inside a class body.

And yes, this uses frame magic, and no, it's not fragile, as it knows the 
difference between class and non-class frames.  The implementation has been 
in Zope for a good few years now, likewise Twisted and PEAK.  It's also 
available as a standalone package from:

     http://cheeseshop.python.org/pypi/DecoratorTools


>but how on earth is the defop syntax of the @defop decorator going to
>generate this?

     @defop(flattening.flatten)
     def flatten_btree(bt:BinaryTree):
         # ... do stuff to bt

Of course, this assumes that flattening.flatten.__addmethod__() is smart 
enough to pull the type information off the first argument, if the third 
addmethod() argument is None.


>A different line of questioning would be to try to understand how
>Phillip's addmethod and hasmethod are supposed to work. I still hope
>someone will explain it to me.

addmethod registers a method with a generic function.  The only reason we 
even need it as a separate function (as opposed to just assuming a 
__hasmethod__) is to allow retrofitting of existing Python objects (such as 
iter/len/etc.) as generic functions via __special__ method setting.  The 
same applies to hasmethod vs. just using __contains__ or something of that 
sort.

If you want to treat it as a Py3K only thing and have __hasmethod__ and 
__addmethod__ slots for builtin functions, then there's no need to have 
actual addmethod/hasmethod functions.  But that's sort of like saying we 
don't need getattr() any more because we can just use 
type(ob).__getattribute__() now!  :)



More information about the Python-3000 mailing list