[python-uk] Advice on decorator grammar
Stestagg
stestagg at gmail.com
Fri Apr 5 16:46:07 CEST 2013
I've been thinking about this, and the more I see, the more I'm convinced
that this extra gramma would *not* help with generating 'pythonic' code.
Having used decorators as: wrappers, subsituters, dynamic substituters,
dependency injectors, actual decorators and other things (several of these
were mistakes!). I'm reasonably convinced that any pattern that uses
anonymous classes in decorators can be refactored to be structurally more
pythonic.
I'd be happy to be proven wrong (with specific examples), and I agree with
you that, superficially, this seems like a missing feature. However, I
feel that in practice, not allowing such syntax is 'a good thing' (tm)
Yours
Steve
On Thu, Apr 4, 2013 at 5:32 PM, Simon Yarde <simonyarde at me.com> wrote:
> 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
>
> _______________________________________________
> python-uk mailing list
> python-uk at python.org
> http://mail.python.org/mailman/listinfo/python-uk
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-uk/attachments/20130405/74e868b4/attachment.html>
More information about the python-uk
mailing list