【问题标题】:Why is applying a Python decorator with and without parenthesis different?为什么应用带括号和不带括号的 Python 装饰器不同?
【发布时间】:2017-04-03 15:41:52
【问题描述】:

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

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

我的装饰师:

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

Attributes 类(真正的装饰器):

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)时,我们可以看到这些结果:

MyClass(message="")

Method1 =

方法2()

Method4()

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

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

所以我的问题:

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

pyAttributes是我写的一组属性(真实属性,没有Python属性)。它们的行为几乎类似于 .NET 中的

【问题讨论】:

  • 与大多数装饰定义一样,@test 等同于@Test() 的含义,没有参数。例如,我使用 Dart 语言中的装饰器,因为它需要利用它们与前端标记进行交互。我必须对注释及其用途以及它们的用途/用途进行大量研究
  • 装饰器评估一个表达式(@ 之后的那个)并调用该表达式的结果,将装饰项作为参数传递并用返回值替换它。 funcfunc() 不同,所以 @func@func() 不同。
  • click.pocoo.org/5 顺便说一句,在不尝试将 .NET 惯用语融入 Python 的情况下执行自述文件中描述的方法。
  • @Ryan 是的,@deco \n class foo: 被翻译成class foo: \n foo = deco(foo)。我注意到 PyCharm 将基于函数的装饰器涂成蓝色,将基于类的装饰器涂成红色。除此之外,用户应该如何知道如何调用/应用装饰器?
  • 是的。对于任何expr@expr class foo:foo = (expr)(foo),所以deco()(foo) 是正确的。

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


【解决方案1】:

正如@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 用户注意事项:

看彩色的@ 标志:

  • 蓝色 -> 基于函数的装饰器
  • red -> 基于类的装饰器

【讨论】:

    猜你喜欢
    • 2016-06-05
    • 1970-01-01
    • 2020-07-02
    • 2011-03-07
    • 2017-10-17
    • 1970-01-01
    • 2020-04-17
    • 2020-02-26
    相关资源
    最近更新 更多