Python 装饰器@func().attribute 语法错误
Posted
技术标签:
【中文标题】Python 装饰器@func().attribute 语法错误【英文标题】:Python decorator @func().attribute syntax error 【发布时间】:2015-12-10 14:49:39 【问题描述】:我试图在这里找到答案,但找不到。
@obj.func # works
@obj.func(**kwargs) #works
@obj.func1(**kwargs).func2 #-> syntax error
我不明白为什么第三种形式是 SyntaxError,对我来说这似乎没有违反任何 python 语法,而且我很清楚用户想要做什么(参见下面的示例)。
我查看了装饰器实现的pep 0318,但没有找到任何答案。
下面是一个使用示例:
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
你可以使用 ItemFunc 作为装饰器:
@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
但是为什么禁止使用以下“快捷语法”:
@redcross.copy(color="blue").caller
def plot_data2(**kwargs):
pass
但我可以:
@redcross.copy_and_decorate(color="blue")
def plot_data2(**kwargs):
pass
第一种形式看起来更好,至少我更了解背后的意图。
【问题讨论】:
【参考方案1】:Function definitions grammar 不允许调用带有更多点名的电话;语法仅限于带点的名称和一个可选的调用在结尾:
decorated ::= decorators (classdef | funcdef)
decorators ::= decorator+
decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
funcdef ::= "def" funcname "(" [parameter_list] ")" ":" suite
dotted_name ::= identifier ("." identifier)*
请注意,这不是一个完整的表达式,而是一个非常有限的子集。
这与 PEP 相呼应,其中指出:
装饰器语句在它可以接受的范围内是有限的——任意表达式都不起作用。 Guido 更喜欢这个,因为直觉 [17]。
和
拥有一个返回装饰器的函数的基本原理是@符号之后的部分可以被认为是一个表达式(尽管在语法上仅限于一个函数),以及该表达式返回的任何内容叫做。请参阅声明参数 [16]。
强调我的。
理由是Guido feels there isn't a real use case for allowing more:
因此,虽然将语法更改为 @test 在 未来,我想坚持使用更受限制的形式,除非是真正的 提出了允许@test 会增加可读性的用例。 (@foo().bar() 不算数,因为我不认为你会需要 那个)。
您必须让 Guido 和其他核心开发人员相信您的案例是一个合适的用例,值得解除这些限制!
【讨论】:
是的,这更像是一个哲学问题。对我来说,用 .getter 或 .caller 之类的东西完成装饰器(就像在我的示例中一样)清楚地表明了我们想要做什么,同时允许调用最后发送 .caller 方法(我的示例再次)的函数,但更少可读。你不觉得吗? @user3240484:我可以看到你的设置要去哪里;但也许不是使用属性,而是让__call__
返回实际的装饰器,这也支持__call__
?
你是对的,但是如果你看一下我的例子,call 方法已经像'fcall'函数的装饰器一样,为用户保持对象函数式.我可以用调用函数替换 call 并使用 call 来返回装饰器,但是我在那里获得的东西我把它放在那里了。谢谢
是的,因为你的类是一个装饰器工厂,装饰器 和 包装器合二为一。我建议你拉出包装部分。装饰的结果本身不需要再次用作装饰器,不是吗?【参考方案2】:
正如另一个答案中所解释的,Guido had a "gut feeling" 最初是限制的原因。
在 Python 3.9 中取消了此限制。,允许装饰器成为任何有效的表达式。
Previously, the grammar 装饰器是:
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
在 Python 3.9 中,the grammar 被简化为:
decorator: '@' namedexpr_test NEWLINE
这是 3.8 和 3.9 之间唯一的语法变化。详情请参阅PEP 614 -- Relaxing Grammar Restrictions On Decorators。
【讨论】:
以上是关于Python 装饰器@func().attribute 语法错误的主要内容,如果未能解决你的问题,请参考以下文章