Python中的"at"(@)符号有什么作用?

AJ0*_*200 501 python syntax python-decorators

我正在看一些使用该@符号的Python代码,但我不知道它的作用.我也不知道搜索Python文档会搜索什么,或者当@包含符号时Google不会返回相关结果.

Mor*_*lde 315

前言

我承认我花了不少时间才能完全掌握这个概念,所以我会分享我学到的东西,以拯救别人的麻烦.

名称装饰器 - 我们@在函数定义之前使用语法定义的东西- 可能是这里的主要罪魁祸首.

class Pizza(object):
    def __init__(self):
        self.toppings = []

    def __call__(self, topping):
        # When using '@instance_of_pizza' before a function definition
        # the function gets passed onto 'topping'.
        self.toppings.append(topping())

    def __repr__(self):
        return str(self.toppings)

pizza = Pizza()

@pizza
def cheese():
    return 'cheese'
@pizza
def sauce():
    return 'sauce'

print pizza
# ['cheese', 'sauce']
Run Code Online (Sandbox Code Playgroud)

这表明function/ method/ class你以后定义装饰是基本上只是传递作为argumentfunction/ method后立即@迹象.

第一眼看见

微框架Flask从一开始就以下列格式引入装饰器:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"
Run Code Online (Sandbox Code Playgroud)

这反过来转化为:

rule      = "/"
view_func = hello
# They go as arguments here in 'flask/app.py'
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    pass
Run Code Online (Sandbox Code Playgroud)

实现这一点终于让我对Flask感到安宁.

  • 在Flasks的`app.route("/")`的情况下:这个函数返回一个函数,你用`hello()`作为参数调用它 (6认同)
  • 在这里使用装饰器有什么语法或实际好处,而不是(例如)在定义 `hello` 之后立即调用类似 `app.route("/", hello)` 的东西,或者甚至将 `hello` 定义为 lambda `app.route` 的参数?(后一个示例常见于 Node.js `http.Server` 和 Express 路由。) (5认同)
  • 我觉得看起来很酷 (3认同)

Fog*_*ird 262

@行开头的符号用于类,函数和方法装饰器.

在这里阅读更多:

PEP 318:装饰者

Python装饰器

您将遇到的最常见的Python装饰器是:

@属性

@classmethod

@staticmethod

如果你@在一条线的中间看到一个,那就是矩阵乘法.向下滚动以查看解决该问题的其他答案@.

  • 看起来它也可以是矩阵乘法运算符:/sf/answers/1509412551/ (15认同)

Mat*_*ujo 171

此代码段:

def decorator(func):
   return func

@decorator
def some_func():
    pass
Run Code Online (Sandbox Code Playgroud)

相当于这段代码:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)
Run Code Online (Sandbox Code Playgroud)

在装饰器的定义中,您可以添加一些通常不会被函数返回的修改过的东西.

  • 在这行 s"ome_func = Decorator(some_func)" 中,第一个 some_func 是函数 some_func 的变量 =,对吗? (4认同)

小智 121

在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)
Run Code Online (Sandbox Code Playgroud)

此代码产生:

[[18, 14], [62, 66]]
Run Code Online (Sandbox Code Playgroud)

  • 你还有`@ =`(就地)运算符,它是`__imatmul__`. (13认同)
  • 还有其他类似的可重写运算符吗?我知道 `__add__` 和 `__sub__` 分别与 + 和 - 链接,但以前从未听说过 `@` 符号。还有其他人潜伏在那里吗? (3认同)
  • @ThomasKimber 当然。查看 https://docs.python.org/3/reference/datamodel.html?highlight=matmul#emulated-numeric-types 下的所有内容 (2认同)

Aar*_*all 82

Python中的"at"(@)符号有什么作用?

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

在装饰器的上下文中,这个语法:

@decorator
def decorated_function():
    """this function is decorated"""
Run Code Online (Sandbox Code Playgroud)

相当于:

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

decorated_function = decorator(decorated_function)
Run Code Online (Sandbox Code Playgroud)

在矩阵乘法的上下文中,a @ b调用a.__matmul__(b)- 使这种语法:

a @ b
Run Code Online (Sandbox Code Playgroud)

相当于

dot(a, b)
Run Code Online (Sandbox Code Playgroud)

a @= b
Run Code Online (Sandbox Code Playgroud)

相当于

a = dot(a, b)
Run Code Online (Sandbox Code Playgroud)

其中dot,例如,numpy矩阵乘法函数和ab是矩阵.

你怎么能自己发现这个?

我也不知道搜索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
Run Code Online (Sandbox Code Playgroud)

我们可以在这里看到@在三种情况下使用:

  • 装饰
  • 因素之间的运算符
  • 增强赋值运算符

装饰器语法:

Google搜索"decorator python docs"作为最佳结果之一,即"Python语言参考"的"复合语句"部分.向下滚动到函数定义部分,我们可以通过搜索单词"decorator"找到它,我们看到......有很多内容需要阅读.但是,"装饰者"这个词是词汇表的链接,它告诉我们:

装饰

返回另一个函数的函数,通常使用@wrapper语法作为函数转换应用.装饰器的常见例子是classmethod()staticmethod().

装饰器语法只是语法糖,以下两个函数定义在语义上是等价的:

def f(...):
    ...
f = staticmethod(f)

@staticmethod
def f(...):
    ...
Run Code Online (Sandbox Code Playgroud)

类存在相同的概念,但在那里不太常用.有关装饰器的更多信息,请参阅文档中的函数定义和类定义.

所以,我们看到了

@foo
def bar():
    pass
Run Code Online (Sandbox Code Playgroud)

在语义上与以下相同:

def bar():
    pass

bar = foo(bar)
Run Code Online (Sandbox Code Playgroud)

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

(如果这种差异对你的代码的含义产生影响,你应该重新考虑你在生活中做了什么,因为那将是病态的.)

堆积装饰器

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

@f1(arg)
@f2
def func(): pass
Run Code Online (Sandbox Code Playgroud)

大致相当于

def func(): pass
func = f1(arg)(f2(func))
Run Code Online (Sandbox Code Playgroud)

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

如果我们堆叠装饰器,那么定义的函数首先被传递到它上面的装饰器,然后是下一个,依此类推.

这总结了@装饰器上下文中的用法.

运营商, @

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

以下令牌是运营商:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=
Run Code Online (Sandbox Code Playgroud)

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

object.__add__(self, other)
object.__sub__(self, other) 
object.__mul__(self, other) 
object.__matmul__(self, other) 
object.__truediv__(self, other) 
object.__floordiv__(self, other)
Run Code Online (Sandbox Code Playgroud)

[...]这些方法称为执行二进制算术运算(+,-,*,@,/,//,[...]

我们看到这__matmul__对应于@.如果我们在文档中搜索"matmul",我们会 "PEP 465 - 用于矩阵乘法的专用中缀运算符"标题下的"matmul" 链接到Python 3.5中的新内容.

它可以通过定义__matmul__(),__rmatmul__()__imatmul__()用于常规,反射和就地矩阵乘法来实现.

(所以现在我们知道这@=是就地版本).它进一步解释说:

矩阵乘法在数学,科学,工程等许多领域都是一项非常常见的操作,并且@允许编写更清晰的代码:

S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)
Run Code Online (Sandbox Code Playgroud)

代替:

S = dot((dot(H, beta) - r).T,
        dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))
Run Code Online (Sandbox Code Playgroud)

虽然这个运算符可以重载以执行几乎任何操作,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]])
Run Code Online (Sandbox Code Playgroud)

原位矩阵乘法: @=

在研究先前的用法时,我们了解到还有就地矩阵乘法.如果我们尝试使用它,我们可能会发现它尚未实现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'.
Run Code Online (Sandbox Code Playgroud)

当它实现时,我希望结果看起来像这样:

>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])
Run Code Online (Sandbox Code Playgroud)


Alg*_*bra 31

Python中的"at"(@)符号有什么作用?

@符号是一个语法糖python提供利用decorator,
解释问题,它正是装饰器在Python中做什么?

简单地说,decorator允许您修改给定函数的定义而不触及其最内层(它的闭包).
从第三方导入精彩包装时,情况最多.你可以想象它,你可以使用它,但你不能触及它的内心和它的心脏.

这是一个简单的例子,
假设我read_a_book在Ipython上定义了一个函数

In [9]: def read_a_book():
   ...:     return "I am reading the book: "
   ...: 
In [10]: read_a_book()
Out[10]: 'I am reading the book: '
Run Code Online (Sandbox Code Playgroud)

你看,我忘了为它添加一个名字.
如何解决这样的问题?当然,我可以将函数重新定义为:

def read_a_book():
    return "I am reading the book: 'Python Cookbook'"
Run Code Online (Sandbox Code Playgroud)

然而,如果我不被允许操纵原始函数,或者如果有数千个这样的函数要处理,该怎么办呢?

通过思考不同来解决问题并定义一个new_function

def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper
Run Code Online (Sandbox Code Playgroud)

然后使用它.

In [14]: read_a_book = add_a_book(read_a_book)
In [15]: read_a_book()
Out[15]: 'I am reading the book: Python Cookbook'
Run Code Online (Sandbox Code Playgroud)

田田,你看,我修改后read_a_book没有触及内部封闭.没有什么能阻止我装备decorator.

怎么样? @

@add_a_book
def read_a_book():
    return "I am reading the book: "
In [17]: read_a_book()
Out[17]: 'I am reading the book: Python Cookbook'
Run Code Online (Sandbox Code Playgroud)

@add_a_book这是一种花哨而方便的说法read_a_book = add_a_book(read_a_book),它是一种语法糖,没有什么比这更好的了.

  • 这绝对是整个页面中最好的页面,您解释得很好,只有在阅读您的答案后我才能理解它!太棒了! (3认同)

f__*_*ety 15

如果你指的是使用Numpy库的python笔记本中的一些代码,那么@ operator意味着Matrix Multiplication.例如:

import numpy as np
def forward(xi, W1, b1, W2, b2):
    z1 = W1 @ xi + b1
    a1 = sigma(z1)
    z2 = W2 @ a1 + b2
    return z2, a1
Run Code Online (Sandbox Code Playgroud)


iun*_*n1x 14

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

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)
Run Code Online (Sandbox Code Playgroud)

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

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

    @classmethod
    def some_class_method(cls):
        print("this is class method")
Run Code Online (Sandbox Code Playgroud)

一般语法和可能的实现

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

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

@some_decorator
def decorated_function():
    pass
Run Code Online (Sandbox Code Playgroud)

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

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)
Run Code Online (Sandbox Code Playgroud)

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

作为函数

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

通用模式如下:

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
Run Code Online (Sandbox Code Playgroud)

作为一个班

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

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

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
Run Code Online (Sandbox Code Playgroud)

参数化装饰器

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

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
Run Code Online (Sandbox Code Playgroud)

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

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo
Run Code Online (Sandbox Code Playgroud)

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

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar
Run Code Online (Sandbox Code Playgroud)

最后让我们看看带有属性的装饰器。

特性

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

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
    )
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)


小智 8

从Python 3.5开始,'@'用作MATRIX MULTIPLICATION的专用中缀符号(PEP 0465 - 请参阅https://www.python.org/dev/peps/pep-0465/)


小智 7

Python 装饰器就像函数或类的包装器。它\xe2\x80\x99s仍然太概念化了。

\n
def function_decorator(func):\n    def wrapped_func():\n        # Do something before the function is executed\n        func()\n        # Do something after the function has been executed\n    return wrapped_func\n
Run Code Online (Sandbox Code Playgroud)\n

上面的代码是装饰函数的装饰器的定义。\nfunction_decorator 是装饰器的名称。

\n

wrapped_func是内部函数的名称,实际上只在这个装饰器定义中使用。func是被修饰的函数。\n在内部函数wrapped_func中,我们可以在调用func之前和之后执行任何操作。定义了装饰器之后,我们简单的使用它,如下所示。

\n
@function_decorator\ndef func():\n    pass\n
Run Code Online (Sandbox Code Playgroud)\n

然后,每当我们调用函数func时,我们在装饰器中定义的行为也会被执行。

\n

例子 :

\n
from functools import wraps\n\ndef mydecorator(f):\n    @wraps(f)\n    def wrapped(*args, **kwargs):\n        print "Before decorated function"\n        r = f(*args, **kwargs)\n        print "After decorated function"\n        return r\n    return wrapped\n\n@mydecorator\ndef myfunc(myarg):\n    print "my function", myarg\n    return "return value"\n\nr = myfunc('asdf')\nprint r\n
Run Code Online (Sandbox Code Playgroud)\n

输出 :

\n
    Before decorated function\n    my function asdf\n    After decorated function\n    return value\n
Run Code Online (Sandbox Code Playgroud)\n


小智 7

@可以是数学运算符或装饰器,但你的意思是装饰器。

这段代码:

def func(f):
    return f

func(lambda :"HelloWorld")()
Run Code Online (Sandbox Code Playgroud)

使用装饰器可以写成:

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

name()
Run Code Online (Sandbox Code Playgroud)

装饰器可以有参数。

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


Pet*_*ell 5

它表明您正在使用装饰器。这是布鲁斯·埃克尔( Bruce Eckel) 2008 年的例子