为什么应用带有和不带括号的Python装饰器是不同的?(Why is applying a Python decorator with and without parenthesis differen

编程入门 行业动态 更新时间:2024-10-18 12:28:23
为什么应用带有和不带括号的Python装饰器是不同的?(Why is applying a Python decorator with and without parenthesis different?)

我有一个自行编写的基于类的Python装饰器。 据我所见,有一个区别,当我将装饰器应用于类中的方法时。 通常,像@property @staticmethod , @property @staticmethod , @property @unique或@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)时,我们可以看到这些结果:

class MyClass (message =“”)

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

方法2()

方法4()

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

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

所以我的问题:

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

pyAttributes是我写的一组属性(真实属性,没有Python属性)。 它们的行为几乎就像.NET中的自定义属性

I have a self-written class-based Python decorator. As far as I can see there is a difference, when I apply the decorator to a method in a class. Usually, decorators like @classmethod, @staticmethod, @property or @unique are applied without parenthesis. These decorators expect no parameters and are (mostly?) written as function-based decorators.

So in contrast to these examples, my decorator is class-based and expects an optional parameter while applying.

My decorator:

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

The Attributes class (the real decorator):

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__

Example 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"

The attached information is used in a custom autodoc-skip-member handler to decide if a method shall be documented or skipped. This is similar to the docit extension.

So when we look at the generated documentation (Sphinx), we can see these results:

class MyClass(message="")

Method1 = <lib.SphinxExtensions.DocumentMemberAttribute object at 0x0000000004AD9E80>

Method2()

Method4()

What can we see here:

Method1 has no parenthesis indicating a function/method, hence it's regarded a class field and Sphinx uses __str__ (or __repr__?) to document the initial value Method3 is not documented as intended.

So my questions:

Why is there a difference in the behavior? MUST I apply class-based attributes with parenthesis? How should a user know how to use these 2 kinds of decorators? (I can document it for myself, but others might remember just the name und forget to add the parenthesis, because other decorators don't require them.)

pyAttributes is a set of attributes (real attributes, no Python attributes) written by me. They behave almost like custom-attributes in .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 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用户请注意:

看看彩色的@符号:

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

As @Ryan pointed out, the string behind the @ sign is an expression, which gets translated to a function call. The parameter of that call is the object, to which the decorator was applied.

Example 1 - function-based decorators:

I'll use the enum.unique decorator. It is written as a function.

from enum import Enum, unique @unique class MyEnum(Enum): foo = 0 bar = 1

Gets translated to

from enum import Enum, unique class MyEnum(Enum): foo = 0 bar = 1 MyEnum = unique(MyEnum)

Example 2 - class-based decorators:

I'll use the decorator from the question. It is written as a class.

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

Gets translated to

class MyClass: def Method1(): pass Method1 = DocumentMemberAttribute()(Method1) def Method2(): pass Method2 = DocumentMemberAttribute(True)(Method2)

Please note the empty parenthesis before passing Method1 as an argument to the class' __call__ method. These are the initializer's (__init__) parenthesis. So these parenthesis get filled for Method2 when passing True as an argument.

So in conclusion:

function-based decorators are applied without parenthesis class-based decorators are applied with parenthesis

Note for PyCharm users:

Look at the colored @ sign:

blue -> function-based decorator red -> class-based decorator

更多推荐

本文发布于:2023-08-07 11:04:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1464479.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:括号   不带   Python   parenthesis   decorator

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!