@符号在Python中做什么?


当前回答

Python中添加了修饰符,以使函数和方法包装(一种接收函数并返回增强函数的函数)更易于阅读和理解。最初的用例是能够将方法定义为类方法或静态方法。如果没有decorator语法,它将需要一个相当稀疏和重复的定义:

class WithoutDecorators:
def some_static_method():
    print("this is static method")
some_static_method = staticmethod(some_static_method)

def some_class_method(cls):
    print("this is class method")
some_class_method = classmethod(some_class_method)

如果decorator语法用于相同目的,则代码更短,更易于理解:

class WithDecorators:
    @staticmethod
    def some_static_method():
        print("this is static method")

    @classmethod
    def some_class_method(cls):
        print("this is class method")

一般语法和可能的实现

装饰器通常是一个命名对象(不允许使用lambda表达式),它在调用时接受一个参数(它将是装饰函数)并返回另一个可调用对象。这里使用“Callable”而不是有预谋的“function”。虽然装饰器通常在方法和函数的范围内讨论,但它们并不局限于它们。事实上,任何可调用的对象(任何实现_call__方法的对象都被认为是可调用的)都可以用作装饰器,它们返回的对象通常不是简单的函数,而是实现自己__call__方法的更复杂类的更多实例。

decorator语法只是一种语法糖。考虑以下装饰器用法:

@some_decorator
def decorated_function():
    pass

这总是可以通过显式的装饰器调用和函数重新分配来替换:

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)

然而,如果在单个函数上使用多个修饰符,则后者的可读性较差,也很难理解。装饰器可以多种不同的方式使用,如下所示:

作为一项功能

有许多方法可以编写自定义修饰符,但最简单的方法是编写一个函数,该函数返回一个子函数,该子函数包装原始函数调用。

通用模式如下:

def mydecorator(function):
    def wrapped(*args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result
    # return wrapper as a decorated function
    return wrapped

作为一个班级

虽然装饰器几乎总是可以使用函数实现,但在某些情况下,使用用户定义的类是更好的选择。当装饰器需要复杂的参数化或取决于特定状态时,这通常是正确的。

作为类的非参数化装饰器的通用模式如下:

class DecoratorAsClass:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = self.function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result

参数化装饰器

在实际代码中,通常需要使用可参数化的修饰符。当函数用作装饰器时,解决方案很简单——必须使用第二层包装。下面是一个装饰器的简单示例,它在每次调用装饰函数时都会重复执行指定次数的装饰函数:

def repeat(number=3):
"""Cause decorated function to be repeated a number of times.

Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""
def actual_decorator(function):
    def wrapper(*args, **kwargs):
        result = None
        for _ in range(number):
            result = function(*args, **kwargs)
        return result
    return wrapper
return actual_decorator

以这种方式定义的装饰器可以接受参数:

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo

注意,即使参数化装饰器的参数具有默认值,其名称后面的括号也是必需的。将前面的修饰符与默认参数一起使用的正确方法如下:

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar

最后,让我们看看具有财产的装饰器。

财产

财产提供了一个内置的描述符类型,该类型知道如何将属性链接到一组方法。属性接受四个可选参数:fget、fset、fdel和doc。最后一个可以用来定义链接到属性的文档字符串,就像它是一个方法一样。下面是一个Rectangle类的示例,它可以通过直接访问存储两个角点的属性或使用宽度和高度财产进行控制:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def _width_get(self):
        return self.x2 - self.x1

    def _width_set(self, value):
        self.x2 = self.x1 + value

    def _height_get(self):
        return self.y2 - self.y1

    def _height_set(self, value):
        self.y2 = self.y1 + value

    width = property(
        _width_get, _width_set,
        doc="rectangle width measured from left"
    )
    height = property(
        _height_get, _height_set,
        doc="rectangle height measured from top"
    )

    def __repr__(self):
        return "{}({}, {}, {}, {})".format(
            self.__class__.__name__,
            self.x1, self.y1, self.x2, self.y2
    )

创建财产的最佳语法是将属性用作装饰器。这将减少类内方法签名的数量并使代码更加可读和可维护。对于decorator,上述类变为:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    @property
    def width(self):
        """rectangle height measured from top"""
        return self.x2 - self.x1

    @width.setter
    def width(self, value):
        self.x2 = self.x1 + value

    @property
    def height(self):
        """rectangle height measured from top"""
        return self.y2 - self.y1

    @height.setter
    def height(self, value):
        self.y2 = self.y1 + value

其他回答

“at”(@)符号在Python中做什么?

简而言之,它用于装饰器语法和矩阵乘法。

在decorator的上下文中,此语法:

@decorator
def decorated_function():
    """this function is decorated"""

相当于:

def decorated_function():
    """this function is decorated"""

decorated_function = decorator(decorated_function)

在矩阵乘法的上下文中,a@b调用a.__matmul__(b)-生成以下语法:

a @ b

相当于

dot(a, b)

and

a @= b

相当于

a = dot(a, b)

例如,其中dot是numpy矩阵乘法函数,a和b是矩阵。

你怎么能自己发现这一点?

我也不知道要搜索什么,因为搜索Python文档或Google在包含@符号时不会返回相关结果。

如果您想对特定的python语法有一个相当完整的视图,请直接查看语法文件。对于Python 3分支:

~$ grep -C 1 "@" cpython/Grammar/Grammar 

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
--
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
            '<<=' | '>>=' | '**=' | '//=')
--
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power

我们可以在这里看到@用于三种上下文:

装饰工因子之间的运算符增广赋值算子

Decorator语法:

在谷歌搜索“decorator python docs”时,最重要的结果之一是“python语言参考”的“复合语句”部分。向下滚动到函数定义部分,我们可以通过搜索单词“decorater”找到该部分,我们看到。。。有很多书要读。但单词“decorator”是词汇表的链接,它告诉我们:

室内装修设计师返回另一个函数的函数,通常使用@wrapper语法作为函数转换应用。常见的装饰器的示例有classmethod()和staticmethod(()。decorator语法只是语法糖,以下两个函数定义在语义上是等价的:定义f(…):...f=静态方法(f)@静态方法定义f(…):...类也存在相同的概念,但在那里不太常用。有关函数定义和类定义,请参阅文档了解有关装饰器的更多信息。

所以,我们看到了

@foo
def bar():
    pass

在语义上与:

def bar():
    pass

bar = foo(bar)

它们并不完全相同,因为Python使用decorator(@)语法在bar之前计算foo表达式(可以是点查找和函数调用),但在另一种情况下,在bar之后计算foo。

(如果这种差异对代码的含义产生了影响,你应该重新考虑你的生活,因为这是病态的。)

堆叠式装饰器

如果我们回到函数定义语法文档,我们会看到:

@f1(参数)@f2(f2)def func():传递大致相当于def func():传递函数=f1(参数)(f2(函数))

这是一个演示,我们可以首先调用一个装饰器函数,以及堆栈装饰器。在Python中,函数是第一类对象,这意味着您可以将函数作为参数传递给另一个函数,并返回函数。装饰师同时做这两件事。

如果我们堆叠装饰器,那么函数(如定义的那样)首先传递给紧挨着它上面的装饰器,然后传递给下一个,依此类推。

这个about总结了装饰器上下文中@的用法。

操作员@

在语言参考的词法分析部分,我们有一个关于运算符的部分,其中包括@,这使它也是一个运算符:

以下标记是运算符:+ - * ** / // % @<< >> & | ^ ~< > <= >= == !=

在下一页的数据模型中,我们有模拟数字类型一节,

对象__添加__(自己,其他)对象__sub__(自己,其他)对象__多__(自己,其他)对象__matmul__(自己,其他)对象__truediv__(自己,其他)对象__floordiv__(自己,其他)[...]调用这些方法来实现二进制算术运算(+,-,*,@,/,//,[…]

我们看到__matmul__对应于@。如果我们在文档中搜索“matmul”,我们会在标题“PEP465-用于矩阵乘法的专用中缀运算符”下找到Python 3.5新增内容的链接。

它可以通过定义__matmul__()、__rmatmul__()和__imatmul__()用于正则、反射和就地矩阵乘法。

(现在我们了解到@=是就地版本)。它进一步解释了:

矩阵乘法在许多领域中是一种非常常见的运算数学、科学、工程以及@allows的添加编写清洁器代码:S=(H@beta-r).T@inv(H@V@H.T)@(H@beta-r)而不是:S=点((点(H,β)-r).T,dot(inv(dot(点(H,V),H.T)),dot(H,beta)-r))

虽然这个运算符可以重载,几乎可以做任何事情,例如,在numpy中,我们可以使用这个语法来计算数组和矩阵的内积和外积:

>>> from numpy import array, matrix
>>> array([[1,2,3]]).T @ array([[1,2,3]])
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
>>> array([[1,2,3]]) @ array([[1,2,3]]).T
array([[14]])
>>> matrix([1,2,3]).T @ matrix([1,2,3])
matrix([[1, 2, 3],
        [2, 4, 6],
        [3, 6, 9]])
>>> matrix([1,2,3]) @ matrix([1,2,3]).T
matrix([[14]])

就地矩阵乘法:@=

在研究先前的用法时,我们了解到还有原地矩阵乘法。如果我们尝试使用它,我们可能会发现它尚未为numpy实现:

>>> m = matrix([1,2,3])
>>> m @= m.T
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: In-place matrix multiplication is not (yet) supported. Use 'a = a @ b' instead of 'a @= b'.

当它实现时,我希望结果如下:

>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])

Python中添加了修饰符,以使函数和方法包装(一种接收函数并返回增强函数的函数)更易于阅读和理解。最初的用例是能够将方法定义为类方法或静态方法。如果没有decorator语法,它将需要一个相当稀疏和重复的定义:

class WithoutDecorators:
def some_static_method():
    print("this is static method")
some_static_method = staticmethod(some_static_method)

def some_class_method(cls):
    print("this is class method")
some_class_method = classmethod(some_class_method)

如果decorator语法用于相同目的,则代码更短,更易于理解:

class WithDecorators:
    @staticmethod
    def some_static_method():
        print("this is static method")

    @classmethod
    def some_class_method(cls):
        print("this is class method")

一般语法和可能的实现

装饰器通常是一个命名对象(不允许使用lambda表达式),它在调用时接受一个参数(它将是装饰函数)并返回另一个可调用对象。这里使用“Callable”而不是有预谋的“function”。虽然装饰器通常在方法和函数的范围内讨论,但它们并不局限于它们。事实上,任何可调用的对象(任何实现_call__方法的对象都被认为是可调用的)都可以用作装饰器,它们返回的对象通常不是简单的函数,而是实现自己__call__方法的更复杂类的更多实例。

decorator语法只是一种语法糖。考虑以下装饰器用法:

@some_decorator
def decorated_function():
    pass

这总是可以通过显式的装饰器调用和函数重新分配来替换:

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)

然而,如果在单个函数上使用多个修饰符,则后者的可读性较差,也很难理解。装饰器可以多种不同的方式使用,如下所示:

作为一项功能

有许多方法可以编写自定义修饰符,但最简单的方法是编写一个函数,该函数返回一个子函数,该子函数包装原始函数调用。

通用模式如下:

def mydecorator(function):
    def wrapped(*args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result
    # return wrapper as a decorated function
    return wrapped

作为一个班级

虽然装饰器几乎总是可以使用函数实现,但在某些情况下,使用用户定义的类是更好的选择。当装饰器需要复杂的参数化或取决于特定状态时,这通常是正确的。

作为类的非参数化装饰器的通用模式如下:

class DecoratorAsClass:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = self.function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result

参数化装饰器

在实际代码中,通常需要使用可参数化的修饰符。当函数用作装饰器时,解决方案很简单——必须使用第二层包装。下面是一个装饰器的简单示例,它在每次调用装饰函数时都会重复执行指定次数的装饰函数:

def repeat(number=3):
"""Cause decorated function to be repeated a number of times.

Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""
def actual_decorator(function):
    def wrapper(*args, **kwargs):
        result = None
        for _ in range(number):
            result = function(*args, **kwargs)
        return result
    return wrapper
return actual_decorator

以这种方式定义的装饰器可以接受参数:

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo

注意,即使参数化装饰器的参数具有默认值,其名称后面的括号也是必需的。将前面的修饰符与默认参数一起使用的正确方法如下:

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar

最后,让我们看看具有财产的装饰器。

财产

财产提供了一个内置的描述符类型,该类型知道如何将属性链接到一组方法。属性接受四个可选参数:fget、fset、fdel和doc。最后一个可以用来定义链接到属性的文档字符串,就像它是一个方法一样。下面是一个Rectangle类的示例,它可以通过直接访问存储两个角点的属性或使用宽度和高度财产进行控制:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def _width_get(self):
        return self.x2 - self.x1

    def _width_set(self, value):
        self.x2 = self.x1 + value

    def _height_get(self):
        return self.y2 - self.y1

    def _height_set(self, value):
        self.y2 = self.y1 + value

    width = property(
        _width_get, _width_set,
        doc="rectangle width measured from left"
    )
    height = property(
        _height_get, _height_set,
        doc="rectangle height measured from top"
    )

    def __repr__(self):
        return "{}({}, {}, {}, {})".format(
            self.__class__.__name__,
            self.x1, self.y1, self.x2, self.y2
    )

创建财产的最佳语法是将属性用作装饰器。这将减少类内方法签名的数量并使代码更加可读和可维护。对于decorator,上述类变为:

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    @property
    def width(self):
        """rectangle height measured from top"""
        return self.x2 - self.x1

    @width.setter
    def width(self, value):
        self.x2 = self.x1 + value

    @property
    def height(self):
        """rectangle height measured from top"""
        return self.y2 - self.y1

    @height.setter
    def height(self, value):
        self.y2 = self.y1 + value

在Python 3.5中,可以将@重载为运算符。它被命名为__matmul__,因为它被设计用于矩阵乘法,但它可以是任何你想要的。详见PEP465。

这是矩阵乘法的简单实现。

class Mat(list):
    def __matmul__(self, B):
        A = self
        return Mat([[sum(A[i][k]*B[k][j] for k in range(len(B)))
                    for j in range(len(B[0])) ] for i in range(len(A))])

A = Mat([[1,3],[7,5]])
B = Mat([[6,8],[4,2]])

print(A @ B)

此代码产生:

[[18, 14], [62, 66]]

@可以是数学运算符或DECORATOR,但您的意思是DECORATOR。

此代码:

def func(f):
    return f

func(lambda :"HelloWorld")()

使用decorator可以写成:

def func(f):
    return f
@func
def name():
    return "Hello World"

name()

装饰器可以有参数。

你可以看到这个GeeksforGeeks帖子:https://www.geeksforgeeks.org/decorators-in-python/

用不同的方式来表达别人的想法:是的,这是一个装饰师。

在Python中,它类似于:

创建函数(在@call下面)调用另一个函数对创建的函数进行操作。这将返回一个新函数。您调用的函数是@的参数。用返回的新函数替换定义的函数。

这可以用于各种有用的事情,因为函数是对象,只是必要的指令。