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

@符号在Python中做什么?

606532 次浏览

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

一行开始处的@符号用于类和函数装饰者

最常见的Python装饰器是:

行的中间中的@可能是矩阵乘法:

此代码片段:

def decorator(func):return func
@decoratordef some_func():pass

等效于此代码:

def decorator(func):return func
def some_func():pass
some_func = decorator(some_func)

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

示例

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()
@pizzadef cheese():return 'cheese'@pizzadef sauce():return 'sauce'
print pizza# ['cheese', 'sauce']

这表明您在装饰者之后定义的function/method/class基本上只是在@符号之后立即作为argument传递给function/method

第一次目击

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

from flask import Flaskapp = 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感到平静。

用不同的方式说别人有什么:是的,这是一个装饰。

在Python中,它就像:

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

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

在Python 3.5中,您可以将@重载为运算符。它被命名为__matmul__,因为它旨在进行矩阵乘法,但它可以是您想要的任何东西。有关详细信息,请参阅PEP465

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

class Mat(list):def __matmul__(self, B):A = selfreturn 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]]

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

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

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

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

@decoratordef 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是矩阵。

你怎么能自己发现这个?

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

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

~$ grep -C 1 "@" cpython/Grammar/Grammar
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINEdecorators: decorator+--testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |'<<=' | '>>=' | '**=' | '//=')--arith_expr: term (('+'|'-') term)*term: factor (('*'|'@'|'/'|'%'|'//') factor)*factor: ('+'|'-'|'~') factor | power

我们可以在这里看到@在三个上下文中使用:

  • 装饰者
  • 因子之间的操作符
  • 增强赋值操作符

装饰器语法:

在谷歌上搜索“装饰器python文档”会得到“Python语言参考”的“复合语句”部分,这是最热门的结果之一。向下滚动到函数定义部分,我们可以通过搜索“装饰器”这个词来找到,我们看到……有很多东西要读。但是这个词,“装饰器”是指向词汇表的链接,它告诉我们:

装饰者

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

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

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

类也存在相同的概念,但在那里不太常用。查看函数定义和类定义的留档更多关于装饰的信息

所以,我们看到

@foodef bar():pass

在语义上与:

def bar():pass
bar = foo(bar)

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

(如果这种差异使你的代码的意义有所不同,你应该重新考虑你在做什么,因为那将是病态的。

堆叠装饰器

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

@f1(arg)@f2def func(): pass

大致相当于

def func(): passfunc = 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]]).Tarray([[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]).Tmatrix([[14]])

原位矩阵乘法:@=

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

>>> m = matrix([1,2,3])>>> m @= m.TTraceback (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>>> mmatrix([[14]])

@符号也用于访问plydata/pandas dataframe查询中的变量,pandas.DataFrame.query。示例:

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

“at”(@)符号在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'

多田,你看,我修改了read_a_book而没有触及它的内部封闭。没有什么能阻止我配备decorator

什么是@

@add_a_bookdef 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是说read_a_book = add_a_book(read_a_book)的一种奇特而方便的方式,它是一种语法糖,没有什么比它更花哨的了。

如果您引用的是使用Numpy库的python笔记本中的某些代码,那么@ operator意味着矩阵乘法。例如:

import numpy as npdef forward(xi, W1, b1, W2, b2):z1 = W1 @ xi + b1a1 = sigma(z1)z2 = W2 @ a1 + b2return z2, a1

在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:@staticmethoddef some_static_method():print("this is static method")
@classmethoddef some_class_method(cls):print("this is class method")

一般语法和可能的实现

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

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

@some_decoratordef decorated_function():pass

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

def decorated_function():passdecorated_function = some_decorator(decorated_function)

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

作为一个函数

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

一般模式如下:

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

作为一个班级

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

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

class DecoratorAsClass:def __init__(self, function):self.function = function
def __call__(self, *args, **kwargs):# do some stuff before the original# function gets calledresult = self.function(*args, **kwargs)# do some stuff after function call and# return the resultreturn 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 = Nonefor _ in range(number):result = function(*args, **kwargs)return resultreturn wrapperreturn actual_decorator

这样定义的装饰器可以接受参数:

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

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

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

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

属性

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

class Rectangle:def __init__(self, x1, y1, x2, y2):self.x1, self.y1 = x1, y1self.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, y1self.x2, self.y2 = x2, y2
@propertydef width(self):"""rectangle height measured from top"""return self.x2 - self.x1
@width.setterdef width(self, value):self.x2 = self.x1 + value
@propertydef height(self):"""rectangle height measured from top"""return self.y2 - self.y1
@height.setterdef height(self, value):self.y2 = self.y1 + value

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

def function_decorator(func):def wrapped_func():# Do something before the function is executedfunc()# Do something after the function has been executedreturn wrapped_func

上面的代码是装饰函数的装饰器的定义。function_decorator是设计师的名字

wrapped_func是内部函数的名称,实际上仅在此装饰器定义中使用。func是正在装饰的函数。在内部函数wrapped_func中,我们可以在调用func之前和之后执行任何操作。定义装饰器后,我们只需按如下方式使用它。

@function_decoratordef func():pass

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

示例:

from functools import wraps
def mydecorator(f):@wraps(f)def wrapped(*args, **kwargs):print "Before decorated function"r = f(*args, **kwargs)print "After decorated function"return rreturn wrapped
@mydecoratordef myfunc(myarg):print "my function", myargreturn "return value"
r = myfunc('asdf')print r

输出:

    Before decorated functionmy function asdfAfter decorated functionreturn value

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

此代码:

def func(f):return f
func(lambda :"HelloWorld")()

使用装饰器可以这样写:

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

装饰者可以有争论。

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