From: Stephen Hansen on 2 Jul 2010 13: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. Thanks. -- ... Stephen Hansen ... Also: Ixokai ... Mail: me+list/python (AT) ixokai (DOT) io ... Blog: http://meh.ixokai.io/
From: Thomas Jollans on 2 Jul 2010 14:55 On 07/02/2010 07:41 PM, Stephen Hansen 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 Looks good! You may still want to use functools.update_wrapper or functools.wraps on "wrap". PS: if you weren't stuck on 2.5, but were using 3.x, there's all kinds of fun stuff you could do with function annotations ;-) > > 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. > > Thanks. >
From: Alf P. Steinbach /Usenet on 2 Jul 2010 14:58 * 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>
From: Carl Banks on 2 Jul 2010 16:17 On Jul 2, 10:41 am, Stephen Hansen <me+list/pyt...(a)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
From: Stephen Hansen on 3 Jul 2010 01:31 On 7/2/10 11:55 AM, Thomas Jollans wrote: > Looks good! You may still want to use functools.update_wrapper or > functools.wraps on "wrap". Are you sure? I've been doing a little bit of experimentation and I only did the 'wraps' on that inner function, because it seemed that it was all that was needed to get the propagation of function data I expected. I've since changed it to: def wrapper(fn, *args, **kwargs): print "Calling." result = fn(*args, **kwargs) print "Called." return result return decorator.decorator(wrapper, fn) Because the decorator library includes argspec propagation which was important for other parts of this code which does introspection. (This toy project is, I fully admit, going into very dark and evil places that I have at length advised people against, 'You don't want to do that!'). > PS: if you weren't stuck on 2.5, but were using 3.x, there's all kinds > of fun stuff you could do with function annotations ;-) Oh, believe me, I desperately wish I could go to 3.x. Between function annotations and the new metaclass power (Hi, __prepare__, I love you, please wait for me and I'll marry you when I migrate). I just can't yet. I can't quite figure out why people are all, '3.x brings you nothing'. Yes, the majority of the focus was cleaning, removing edges, but *lord* there's a LOT I am VERY looking forward to using. -- Stephen Hansen ... Also: Ixokai ... Mail: me+list/python (AT) ixokai (DOT) io ... Blog: http://meh.ixokai.io/
|
Next
|
Last
Pages: 1 2 Prev: drag & drop in a python GUI application Next: Importing package with zip-archives |