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

#1 楼

行开头的@符号用于类,函数和方法修饰符。

此处更多信息:

PEP 318:修饰符

Python装饰器

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

@property

@classmethod


@staticmethod

如果在一行的中间看到@,那是另一回事,矩阵乘法。向下滚动以查看其他解决使用@的答案。

评论


看起来它也可以是矩阵乘法运算符:stackoverflow.com/a/21563036/5049813

– Pro Q
18年4月25日在22:47

也可以添加@decorators

– Vijay Panchal
19年8月19日在8:28

#2 楼

示例

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']


这表明您在修饰器之后定义的function / method / class基本上是作为argument传递给function / method之后的@标志。

第一次发现

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

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"


这反过来翻译为:

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


意识到这一点终于使我对Flask感到安心。

评论


对于Flasks的app.route(“ /”):此函数返回一个函数,您可以使用hello()作为参数调用该函数

–已遮罩
18-4-3在20:34



在这里拥有装饰器的语法或实践优势是什么,而不是(例如)在定义hello之后立即调用app.route(“ /”,hello)之类的东西,甚至在app.route的参数中将hello定义为lambda ? (后一个示例在Node.js http.Server和Express路由中很常见。)

–iono
19-10-6在19:21

#3 楼

此代码段:

def decorator(func):
   return func

@decorator
def some_func():
    pass


等效于此代码:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)


在装饰器的定义中您可以添加一些通常不会由函数返回的修改内容。

评论


在此行“ ome_func = decorator(some_func)”中,第一个some_func是对some_func函数的变量=,对吗?

– Viragos
19年7月7日在19:26

#4 楼

在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]]


评论


您还具有@ =(就地)运算符,即__imatmul__。

–波尔GD
18-2-25在12:40

还有其他可重写的运算符吗?我知道__add__和__sub__分别链接到+和-,但是以前从未听说过@符号。还有其他潜伏在那里的人吗?

–托马斯·金伯(Thomas Kimber)
19年7月18日在18:35



@ThomasKimber当然。在docs.python.org/3/reference/…下查看所有内容

–RandyP
6月24日18:10

#5 楼



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

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

@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)




a @= b


等效于

a = dot(a, b)


其中dot是例如numpy矩阵乘法函数,而ab是矩阵。 br />
如何独自发现呢?


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


如果您想对特定python语法的功能有一个比较完整的了解,请直接看一下g rammar文件。对于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语言参考”的“复合语句”部分。向下滚动至有关函数定义的部分,我们可以通过搜索单词“ decorator”来找到该部分,我们发现……有很多东西可供阅读。但是“ decorator”这个词是词汇表的链接,它告诉我们:


decorator

返回另一个函数的函数,通常用作函数使用@wrapper语法进行转换。装饰器的常见
示例是classmethod()staticmethod()

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

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

@staticmethod
def f(...):
    ...


类具有相同的概念,但在该类中使用较少。
有关函数定义和类定义,请参见文档。
有关装饰器的更多信息。


因此,我们看到

@foo
def bar():
    pass


在语义上与以下相同:

def bar():
    pass

bar = foo(bar)


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

(如果这种差异使代码的含义有所不同,则应重新考虑自己的工作方式,因为那样会病理学。)

层叠式装饰器

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


@f1(arg)
@f2
def func(): pass


大致等同于

def func(): pass
func = f1(arg)(f2(func))



这是一个演示,我们可以首先调用一个作为装饰器的函数,以及堆栈装饰器。在Python中,函数是一流的对象-这意味着您可以将一个函数作为参数传递给另一个函数,然后返回函数。装饰器同时完成这两项操作。

如果我们堆叠装饰器,则所定义的函数会首先传递到紧接其上的装饰器,然后传递给下一个,依此类推。 />关于在装饰器上下文中总结@的用法。
@


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


以下标记是运算符:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=



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


object.__add__(self, other)
object.__sub__(self, other) 
object.__mul__(self, other) 
object.__matmul__(self, other) 
object.__truediv__(self, other) 
object.__floordiv__(self, other)


[... ]
调用这些方法来实现二进制算术运算(+-*@///__matmul__对应于@。我们在文档中搜索了“ matmul”,并在标题“ PEP 465-矩阵乘法的专用中缀运算符”下找到了带有“ matmul”的Python 3.5新增功能的链接。


它可以通过定义__matmul__()__rmatmul__()实现,
__imatmul__()定期,反射,就地矩阵乘法。


(所以现在我们得知@=是在位置版本)。它进一步说明:


矩阵乘法是在
数学,科学,工程学和加@允许编写更简洁的代码:

S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)


代替:

S = dot((dot(H, beta) - r).T,
        dot(inv(dot(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]])


#6 楼


Python中的“ @”符号是什么?


@符号是python提供的一种语法糖,它利用decorator来解释问题。 ,这恰恰与装饰器在Python中的功能有关?

将其简单化decorator允许您修改给定函数的定义,而无需触摸其最内层(它是闭包)。
这是最常见的情况从第三方进口精美的包装。您可以可视化它,可以使用它,但不能触摸它的最内层和内心。

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

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


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

def read_a_book():
    return "I am reading the book: 'Python Cookbook'"


但是,如果不允许我操作原始函数,或者如果有成千上万个函数,该怎么办?

通过不同思考解决问题并定义一个new_function
def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper


然后使用它。

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'


Tada,你知道,我修改了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'


@add_a_book是一种奇特而方便的说法,它是一种语法糖,再也没有其他幻想了。

评论


这是整个页面中绝对最好的页面,您解释得很好,只有阅读您的回答后我才能理解!!太棒了!

– MMEL
12月3日7:33

#7 楼

如果您在使用Numpy库的python笔记本中引用某些代码,则@ operator表示矩阵乘法。例如:

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


#8 楼

在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)


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

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

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


常规语法和可能的实现

装饰器通常是一个接受单个对象的命名对象(不允许使用lambda表达式)。参数在被调用时(将是修饰函数)并返回另一个可调用对象。此处使用“可调用”代替带有预想的“功能”。尽管装饰器通常在方法和功能的范围内进行讨论,但它们不限于此。实际上,任何可调用的对象(实现_call__方法的任何对象都被视为可调用的对象)可以用作修饰符,并且它们返回的对象通常不是简单的函数,而是更多复杂类的实例,这些实例实现了自己的__call_方法。 br />
装饰器语法只是一个语法糖。请考虑以下装饰器用法:

@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类的示例,可以通过直接访问存储两个角点的属性或使用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
    )


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

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


#9 楼

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

#10 楼

它表明您正在使用装饰器。这是Bruce Eckel在2008年的例子。

#11 楼

用不同的方式来说明其他人:是的,它是一个装饰器。

在Python中,就像这样:


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

由于函数是对象,只是必要的指示。

#12 楼

@符号还用于访问plydata / pandas数据框查询pandas.DataFrame.query中的变量。
示例:

df = pandas.DataFrame({'foo': [1,2,15,17]})
y = 10
df >> query('foo > @y') # plydata
df.query('foo > @y') # pandas