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

Why is applying a Python decorator with and without parenthesis different?

我有一个自写的基于class的Python装饰器。据我所知,当我将装饰器应用于 class 中的方法时,存在差异。通常,像 @classmethod@staticmethod@property@unique 这样的装饰器是在没有括号的情况下应用的。这些装饰器不需要参数,并且(大部分?)被编写为基于函数的装饰器。

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

我的装饰师:

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

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

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:

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 object at 0x0000000004AD9E80>

Method2()

Method4()

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

所以我的问题:


pyAttributes is a set of attributes (real attributes, no Python attributes) written by me. They behave almost like .NET

正如@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 的装饰器:

我将使用问题中的装饰器。它被写成 class.

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 作为参数传递给 class' __call__ 方法之前的空括号。这些是初始化程序的 (__init__) 括号。因此,当将 True 作为参数传递时,这些括号会填充 Method2

总之:

  • 基于函数的装饰器在没有括号的情况下应用
  • 基于
  • class 的装饰器应用括号

PyCharm 用户注意事项:

看看彩色的 @ 标志:

  • blue -> 基于函数的装饰器
  • red -> class-based decorator