Mangle function name with decorator?

Aaron Brady castironpi at gmail.com
Wed Mar 18 21:08:39 EDT 2009


On Mar 18, 8:47 am, Adam <adam.crossl... at gmail.com> wrote:
> On Mar 17, 1:49 pm, Aaron Brady <castiro... at gmail.com> wrote:
>
>
>
> > You would need a unique attribute to look for on values in the
> > dictionary, which means you'd need to detect what functions you are
> > renaming; possibly by using a decorator to mark them.  (Untested:)
>
> > class X( metaclass= M ):
> >   @mark( 'A' )
> >   def foo( ... ).
>
> > class M( type ):
> >   def __new__( cls, name, bases, namespace ):
> >     for k, v in namespace.items():
> >       if v.tag== 'mark-A':
> >         namespace[ k ]= modification( v )
> > or in your case:
> >         del namespace[ k ]
> >         namespace[ k_mod ]= v.original
>
> > possibly also setting func_name as well.
> >     return type( name, bases, namespace )
>
> I tried this, but unfortunately, it still doesn't allow me to have two
> methods that initially have the same name, because the namespace
> dictionary that the metaclass gets only has one one entry, the last
> one in.  So, if I try code like this:
snip

> The first definition of foo (the one that is not decorated) is over-
> written by the second one, and the metaclass never sees it.

Michele presented a solution to this if you are able to use Python 3.

>  So, I
> think that I understand what you said about storing the functions in a
> hashtable.  That makes sense, as I can keep a reference to the
> function even if ti gets over-written in the class main namespace
> dictionary.  However, I'm not entirely sure about how to do that with
> decorators.

Here is a possiblity.  It's redundant, but it works.

>>> class X:
...     funcs= {}
...     def wrap( funcs ):
...             def inner( fun ):
...                     if fun.__name__ not in funcs:
...                             funcs[ fun.__name__ ]= []
...                     funcs[ fun.__name__ ].append( fun )
...             return inner
...     @wrap( funcs )
...     def foo( self ): pass
...     @wrap( funcs )
...     def foo( self ): pass
...
>>> X.funcs
{'foo': [<function foo at 0x00BA32B8>, <function foo at 0x00BA3300>]}

> Wouldn't the decorator function have to be bound to a
> context to keep a hastable between calls?  I can't us a bound method
> as a decorator, can I?

'wrap' needs a reference to the hash table.  I passed it explicitly,
but more subtle methods might be possible.

For creating the final version of 'foo' that is externally visible,
you can (I presume) just access the hash table as an attribute of the
class/object it's called on.  Or you could set it to a generic
dispatcher.  The two versions are defined and called by the same name,
but stored as different names.

Abstractly, function overloading is a weakness of dynamic typing and
variable argument lists.  In order for them to be compatible, there
needs to be an order of precedence, and a way to specify argument
types.  Decorators and annotations are two solutions.

@typed( FooA )
def jam( arg1 ).
@typed( FooB )
def jam( arg2 ).
@typed( )
def jam( *args ).

If 'jam' is called with a FooA, the first 'jam' is called.  If called
with a FooB, the second is.  If called with any other single
parameter, /or/ multiple parameters, the third is.

It's unlikely you need the full range of "motion" of generic
overloading, especially including variable-length and keyword argument
lists, so you can probably settle for simpler.  There are a couple of
existing solutions out there, such as multi-methods.
http://www.artima.com/weblogs/viewpost.jsp?thread=155514

IMO, defining methods under different names isn't awful.  You want to
call them by the same name.  (Untested.)

class X:
  def foo( self, arg ):
    if isinstance( arg, JamA ): self.foo_jamA( arg )
    if isinstance( arg, JamB ): self.foo.jamB( arg )
    #exception
  def foo_jamA.
  def foo_jamB.

So here's a possibility, and you can probably make 'foo' slightly more
generic.  Some solutions mix the /name/ of the type into the name of
the method automatically.

  def foo( self, arg ):
    getattr( self, 'foo_'+ type( arg ).__name__ )( arg )

So far, I have liked the metaclass __setattr__ and the definition
chaining ideas the best.  However, if you're passing the prior
definition to the wrapper to chain with, it might be simpler to pass a
hash table.  If you subclass the class that contains the hash table,
you can continue to access it:

class X:
    funcs= {}
    @wrap( funcs )
    def foo( self ): pass
class Y( X ):
    @X.wrap( X.funcs )
    def foo( self ): pass



More information about the Python-list mailing list