Decorators, with optional arguments

Alf P. Steinbach /Usenet alf.p.steinbach+usenet at gmail.com
Fri Jul 2 14:58:35 EDT 2010


* Stephen Hansen, on 02.07.2010 19:41:
> Okay, so!
>
> I actually never quite got around to learning to do deep and useful
> magic with decorators. I've only ever done the most basic things with
> them. Its all been a little fuzzy in my head: things like what order
> decorators end up being called in if there's more then one, etc.
>
> But in my current situation, what I'm wanting to do is have a decorator
> that wraps a function but which takes an *optional* argument, and sets
> that argument as an attribute on said function if its there.
>
> Here's what some tweaking and playing around has gotten me, as a recipe:
>
>      import functools
>
>      def my_decorator(arg):
>          if callable(arg): # Stuck on 2.5. Leavemealone. :)
>              protocol = None
>          else:
>              protocol = arg
>
>          def wrap(fn):
>              print "Wrapping."
>              fn.protocol = protocol
>
>              @functools.wraps(fn)
>              def wrapper(*args, **kwargs):
>                  print "Calling."
>                  result = fn(*args, **kwargs)
>                  print "Called."
>                  return result
>
>              return wrapper
>
>          if not protocol: # argument-less decorator
>              print "Calling wrap."
>              return wrap(arg)
>          else:
>              print "Returning wrap."
>              return wrap
>
> To be used as:
>
>      class Thing(object):
>          @expose
>          def test1(self, arg1):
>              return arg1
>
>          @expose("testing")
>          def test2(self, arg2):
>              return arg2
>
> So, my question: am I doing this right? :) Some play-through testing
> appears to work. But, the dizzying array of nested def's up there leaves
> me a bit dazed, so I'm wondering if there's a simpler way to accomplish
> what I'm trying to do.

If you're willing to have slightly more explicit usage code, consider e.g.


<code>
#Py3

import functools

class expose:
     def __init__( self, protocol = None ):
         self._protocol = protocol

     def __call__( self, f ):
         print( "Wrapping." )
         f.protocol = self._protocol

         @functools.wraps( f )
         def wrapper( *args, **kwargs ):
             print( "Calling." )
             result = f( *args, **kwargs )
             print( "Called." )
             return result

         return wrapper

class Thing(object):
     @expose()
     def test1(self, arg1):
         return arg1

     @expose( "testing" )
     def test2(self, arg2):
         return arg2

o = Thing()
print( o.test1( 1.11 ) )
print( o.test2( 2.22 ) )
</code>


Cheers & hth.,

- Alf


-- 
blog at <url: http://alfps.wordpress.com>



More information about the Python-list mailing list