I tried to find an answer here, but could not.
@obj.func # works
@obj.func(**kwargs) #works
@obj.func1(**kwargs).func2 #-> syntax error
I do not understand why the third form is a SyntaxError, it seems for me that is not violating any python syntax and it is clear for me what the user want to do (see example below).
I looked at pep 0318 of decorator implementation but didn't find any answers.
Here bellow, would be an example of use:
class ItemFunc(object):
def __init__(self, fcall=None, **kwargs):
self.defaults = kwargs
self.fcall = None
def __call__(self, *args, **kwargs):
kwargs = dict(self.defaults, **kwargs)
# do something more complex with kwargs
output = self.fcall(*args, **kwargs)
# do something more with output
return output
def caller(self, fcall):
""" set call and return self """
self.call = fcall # after some check obviously
return self
def copy(self,**kwargs):
kwargs = dict(self.defaults, **kwargs)
return self.__class__(self.fcall, **kwargs)
def copy_and_decorate(self, **kwargs):
return self.copy(**kwargs).caller
Than you can use ItemFunc as a decorator:
@ItemFunc
def plot(**kwargs):
pass
redcross = plot.copy(color="red", marker="+")
@redcross.caller
def plot_data1(**kwargs):
pass
bluecross = redcross.copy(color="blue")
@bluecross.caller
def plot_data2(**kwargs):
pass
But why this following 'short cut syntax' is forbidden :
@redcross.copy(color="blue").caller
def plot_data2(**kwargs):
pass
But I can do:
@redcross.copy_and_decorate(color="blue")
def plot_data2(**kwargs):
pass
The first form looks for nicer, at least I understand better the intentions behind.
The Function definitions grammar does not allow for calls with further dotted names; the syntax is limited to dotted names and an optional call at the end:
decorated ::= decorators (classdef | funcdef)
decorators ::= decorator+
decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
funcdef ::= "def" funcname "(" [parameter_list] ")" ":" suite
dotted_name ::= identifier ("." identifier)*
Note that that's not a full expression, but a very limited subset.
This echoes the PEP, which states:
The decorator statement is limited in what it can accept -- arbitrary expressions will not work. Guido preferred this because of a gut feeling [17] .
and
The rationale for having a function that returns a decorator is that the part after the @ sign can be considered to be an expression (though syntactically restricted to just a function), and whatever that expression returns is called. See declaration arguments [16] .
Emphasis mine.
The rationale is that Guido feels there isn't a real use case for allowing more:
So while it would be quite easy to change the syntax to @test in the future, I'd like to stick with the more restricted form unless a real use case is presented where allowing @test would increase readability. (@foo().bar() doesn't count because I don't expect you'll ever need that).
You'll have to convince Guido and the other core developers that your case is a proper usecase worthy of lifting these restrictions!
As explained in the other answer, Guido had a "gut feeling" was the reason for the restriction originally.
This restriction is lifted in Python 3.9., allowing decorators to be any valid expression.
Previously, the grammar for decorators was:
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
In Python 3.9 the grammar is simplified to:
decorator: '@' namedexpr_test NEWLINE
This is the only grammar change between 3.8 and 3.9. See PEP 614 -- Relaxing Grammar Restrictions On Decorators for full details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With