[python-uk] Advice on decorator grammar

Simon Yarde simonyarde at me.com
Thu Apr 4 18:32:29 CEST 2013


Thanks Nick. I wonder if you see any use or validity in an expanded grammar allowing class-initialisation within the decorator syntax?

Or as Stestagg suggests, there is no real practical need for it?

> decoratedfoo.orig(1, 2)             # run original function


Thanks for highlighting decorator-pattern-like-access, with the inner object available from the outer, and perhaps exposing methods beyond __call__ too, i.e. ``decorated.orig.some_method()``.

> @mydecorator
> def foo(a, b):
>    pass
> 
> is identical to
> 
> def foo(a, b):
>    pass
> foo = mydecorator(foo)

I hoped to pick this one up with the example below.. it is presently a workaround to apply an instance-method decorator due to syntax restrictions.

def f():
   pass

f = A('foo').some_process(f)

==

@A('foo').some_process     # raises syntax error
def f():
    pass

On 4 Apr 2013, at 10:16, Nick Murdoch wrote:

> Hi Simon,
> 
> It might be of use to you to know that the decorator syntax is actually a syntactic shortcut for a longer way of writing the same thing.
> 
> For instance,
> 
> @mydecorator
> def foo(a, b):
>    pass
> 
> is identical to
> 
> def foo(a, b):
>    pass
> foo = mydecorator(foo)
> 
> 
> If you wanted to only apply the decorator at certain times, you could call the decorator directly when you need it. There'd be a bit of overhead since you're re-running the decorator function each time, but I'll leave it to you to decide whether that's a problem for your use case.
> 
> For example:
> 
> foo(1, 2)               # runs without decoration
> mydecorator(foo)(1, 2)  # runs with decoration
> 
> You could expand this further, for instance your decorator could expose the original function so you don't have to keep on re-running the decorator.
> 
> decoratedfoo = mydecorator(foo)     # create decorated function
> decoratedfoo(1, 2)                  # run decorated function
> decoratedfoo.orig(1, 2)             # run original function
> 
> Hope that helps,
> 
> Nick
> 
> On Wed, Apr 03, 2013 at 06:42:02PM +0100, Simon Yarde wrote:
>> This may well be moot, so thank you for chipping in. All your suggestions  are completely valid and practical.
>> 
>> And thank you Stestagg and a.cavallo for commenting on references; I've tried to show in the examples below how the instance might be used to store config that is accessed by instance-methods, so external access was not an issue for the scenario I was envisaging.
>> 
>> I'm interested in decorator-methods that can be employed in different scenarios; as 'python decorators'; and using a decorator-pattern for dynamic decoration of callables.
>> 
>> The grammar seems to preclude such flexibility, and a certain elegance.
>> 
>> I'll try to set-out a possible flexible design-pattern that shows the same decorator-method employed flexibly, and where it becomes inelegant or unintuitive.
>> 
>> This works:
>> 
>> def f():
>>    pass
>> 
>> f = A('foo').some_process(f)
>> 
>> This 'could' work, where it not for grammar inconsistency:
>> 
>> @A('foo').some_process
>> def f():
>>    pass
>> 
>> The same pattern enables dynamic decoration using different instances of A:
>> 
>> @apply_some_process_from_one_of_these_at_random(
>>   A('foo'),
>>   A('bar')
>> )
>> def f():
>>    pass
>> 
>>> You could do this by making decorator_method a classmethod:
>>> 
>>> @MyDecorator.decorate_this(foo)
>> 
>> 
>> Using a class-method, I would have to name the method I wanted to call and supply initialisation at the same time, and return a configured callable to perform the desired process:
>> 
>> @apply_some_process_from_one_of_these_at_random(
>>    A.some_process('foo')
>>    A.some_process('bar')
>> )
>> 
>> It's attractive not to have to name the process to be called at configuration, and to be able to store configuration in the instance (this works):
>> 
>> @apply_an_arbitrary_process_from(
>>    A('foo')
>> )
>> 
>> and it would be elegant/consistent to be able to apply a process using the decorator-syntax if need be (this doesn't work because of the grammar):
>> 
>> @A('foo').some_other_process
>> 
>> 
>> On 3 Apr 2013, at 12:43, Stestagg wrote:
>> 
>>> This seems redundant to me, the MyDecorator instance would not be bound to anything, so you'll 'loose' the reference to it, except through the call to decorator_method().  
>>> 
>>> You could do this by making decorator_method a classmethod:
>>> 
>>> class MyDecorator(object):
>>> 
>>>    @classmethod
>>>    def decorate_this(cls, ...):
>>>        pass
>>> 
>>> allowing you to use it:
>>> 
>>> @MyDecorator.decorate_this(foo)
>>> 
>>> If your intent is to pass arguments to the MyDecorator instance, just pass them to the decorator method directly.
>>> 
>>> Finally, if you're trying to implement singleton like behaviour.  (a registry etc..) then using your example of binding an instance of MyDecorator() to a module-level name is sensible.
>>> 
>>> MY_REGISTRY = MyDecorator()
>>> 
>>> @MY_REGISTRY.decoate_this()
>>> def wrapped():
>>>    ...
>>> 
>>> Does your use-case match any of these?
>>> 
>>> Thanks
>>> 
>>> Steve
>>> 
>>> 
>>> On Wed, Apr 3, 2013 at 12:34 PM, Simon Yarde <simonyarde at me.com> wrote:
>>> Hi All
>>> 
>>> I've not posted to this list before. Hello!
>>> 
>>> I have a question about decorators and have failed to devise a search that has thrown up any history of discussion on this particular matter.
>>> 
>>> Does the following seem like something that 'should' work? Or is anyone aware of a source of documentation that explains historically why the following syntax might not be allowed?
>>> 
>>> I hope this sort of conundrum/discussion-point is appropriate to this forum; I'm not on python-dev and this is obviously not a bug.
>>> 
>>> So..
>>> 
>>> Decorator grammar is this:
>>> 
>>> decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
>>> 
>>> The grammar prevents this:
>>> 
>>>>>> class MyDecorator:
>>> ...     def decorator_method():
>>> ...             pass
>>> ...
>>>>>> @MyDecorator().decorator_method()
>>>  File "<stdin>", line 1
>>>    @MyDecorator().decorator_method()
>>>                  ^
>>> SyntaxError: invalid syntax
>>> 
>>> But is possible to achieve the desired effect by assigning the class instance to variable:
>>> 
>>>>>> mydecorator = MyDecorator()
>>> ... @mydecorator.decorator_method
>>> ... def f():
>>> 
>>> 
>>> My initial thoughts were that the syntax provided a neat way to provide a configurable decorator class instance with a number of alternative decorator-function generating methods, rather than just the usual __call__.
>>> 
>>> S
>>> _______________________________________________
>>> python-uk mailing list
>>> python-uk at python.org
>>> http://mail.python.org/mailman/listinfo/python-uk
>>> 
>>> _______________________________________________
>>> python-uk mailing list
>>> python-uk at python.org
>>> http://mail.python.org/mailman/listinfo/python-uk
>> 
>> Simon Yarde
>> 
>> 07525 063 134
>> simonyarde at me.com
>> 
>> _______________________________________________
>> python-uk mailing list
>> python-uk at python.org
>> http://mail.python.org/mailman/listinfo/python-uk
> _______________________________________________
> python-uk mailing list
> python-uk at python.org
> http://mail.python.org/mailman/listinfo/python-uk



More information about the python-uk mailing list