Python装饰器@func()。属性语法错误

时间:2015-09-14 14:37:40

标签: python class attributes decorator python-decorators

我试图在这里找到答案,但不能。

@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         

第一种形式看起来更好,至少我更了解背后的意图。

2 个答案:

答案 0 :(得分:6)

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和其他核心开发人员,你的案例是一个值得解除这些限制的正确用例!

答案 1 :(得分:0)

如另一个答案所述,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