On Jul 2, 10:41 am, Stephen Hansen <me+list/pyt...@ixokai.io> wrote: > 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.
I usually recommend to factoring out magic parts into their own little function, which then invoke the non-magical part in different ways: def expose_as(protocol): 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 return wrap def expose(arg): """Magic function to allow omitting argument.""" if type(arg) is types.FunctionType: print "Calling with arg as function" return expose_as(None)(arg) print "Calling with arg as protocol" return expose_as(arg) I believe it's a good practice to isolate magic so that it's easy to see, and also to ensure there's a non-magical way to do it if that is needed. However, the world won't come to an end if you do it your way. Carl Banks -- http://mail.python.org/mailman/listinfo/python-list