为什么应用带有和不带括号的Python装饰器不同?

时间:2016-11-20 02:05:33

标签: python-3.x custom-data-attribute python-decorators default-parameters

我有一个自编的基于类的Python装饰器。当我将装饰器应用于类中的方法时,我可以看到存在差异。通常,@classmethod@staticmethod@property@unique等装饰器不带括号。这些装饰器不需要参数,并且(主要是?)被编写为基于函数的装饰器。

因此,与这些示例相比,我的装饰器是基于类的,并且在应用时需要一个可选参数。

我的装饰者:

class DocumentMemberAttribute(Attribute):
  def __init__(self, value=True):
    super().__init__()
    self.value = value

属性类(真正的装饰者):

class Attribute:
  __AttributesMemberName__ = "__pyattr__"

  def __call__(self, func):
    self._AppendAttribute(func, self)
    return func

  @staticmethod
  def _AppendAttribute(func, attribute):
    # inherit attributes and append myself or create a new attributes list
    if (Attribute.__AttributesMemberName__ in func.__dict__):
      func.__dict__[Attribute.__AttributesMemberName__].append(attribute)
    else:
      func.__setattr__(Attribute.__AttributesMemberName__, [attribute])

  def __str__(self):
    return self.__name__

示例课程:

class MyClass:
  def __init__(self, message=""):
    super().__init__()
    self.message = message

  @DocumentMemberAttribute
  def Method1(self):
    return "foo"

  @DocumentMemberAttribute()
  def Method2(self):
    return "bar"

  @DocumentMemberAttribute(False)
  def Method3(self):
    return "spam"

  @DocumentMemberAttribute(True)
  def Method4(self):
    return "egg"

附加信息用于自定义autodoc-skip-member处理程序,以确定是否应记录或跳过方法。这类似于docit扩展名。

因此,当我们查看生成的文档(Sphinx)时,我们可以看到这些结果:

  

class MyClass (message =“”)

     
    

Method1 =< lib.SphinxExtensions.DocumentMemberAttribute对象位于0x0000000004AD9E80>

         

方法2()

         

方法4()

  

我们在这里可以看到什么:

  • Method1没有表示函数/方法的括号,因此它被视为一个类字段,而Sphinx使用__str__(或__repr__?)来记录初始值
  • 方法3未按预期记录。

所以我的问题:

  • 为什么行为存在差异?
  • 我必须使用带括号的基于类的属性吗?
  • 用户应该如何知道如何使用这两种装饰器? (我可以为自己记录,但其他人可能只记得名称而忘记添加括号,因为其他装饰者不需要它们。)

pyAttributes是由我编写的一组属性(真实属性,没有Python属性)。它们的行为几乎与.NET中的一样

1 个答案:

答案 0 :(得分:0)

正如@Ryan指出的那样,@符号后面的字符串是一个表达式,它被转换为函数调用。该调用的参数是应用装饰器的对象。

示例1 - 基于函数的装饰器:

我将使用enum.unique装饰器。它是作为一个函数编写的。

from enum import Enum, unique

@unique
class MyEnum(Enum):
  foo = 0
  bar = 1

获取翻译为

from enum import Enum, unique

class MyEnum(Enum):
  foo = 0
  bar = 1

MyEnum = unique(MyEnum)

示例2 - 基于类的装饰器:

我将使用问题中的装饰器。它是作为一个类写的。

class MyClass:
  @DocumentMemberAttribute()
  def Method1():
    pass

  @DocumentMemberAttribute(True)
  def Method2():
    pass

获取翻译为

class MyClass:
  def Method1():
    pass

  Method1 = DocumentMemberAttribute()(Method1)

  def Method2():
    pass

  Method2 = DocumentMemberAttribute(True)(Method2)

在将Method1作为参数传递给班级之前,请注意空括号。 __call__方法。这些是初始化程序(__init__)括号。因此,当传递True作为参数时,这些括号将填充Method2

总之:

  • 基于函数的装饰器不带括号
  • 基于类的装饰器应用括号

PyCharm用户请注意:

查看彩色@标志:

  • 蓝色 - >基于功能的装饰者
  • 红色 - >基于类的装饰者