问题:Python中的“ @”(@)符号有什么作用?

我正在看一些使用 @符号的,但我不知道它的作用。我也不清楚要搜索的内容,因为搜索Python文档时会发现该@符号,否则Google不会返回相关结果。

I’m looking at some Python code which used the @ symbol, but I have no idea what it does. I also do not know what to search for as searching Python docs or Google does not return relevant results when the @ symbol is included.


回答 0

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

在这里阅读更多:

PEP 318:装饰器

Python装饰器

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

@属性

@classmethod

@staticmethod

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

An @ symbol at the beginning of a line is used for class, function and method decorators.

Read more here:

PEP 318: Decorators

Python Decorators

The most common Python decorators you’ll run into are:

@property

@classmethod

@staticmethod

If you see an @ in the middle of a line, that’s a different thing, matrix multiplication. Scroll down to see other answers that address that use of @.


回答 1

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/ 基本上只是作为符号传递到/ 之后的/ 。classargumentfunctionmethod@

第一次发现

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

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和平相处。

Example

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

This shows that the function/method/class you’re defining after a decorator is just basically passed on as an argument to the function/method immediately after the @ sign.

First sighting

The microframework Flask introduces decorators from the very beginning in the following format:

from flask import Flask
app = Flask(__name__)

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

This in turn translates to:

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

Realizing this finally allowed me to feel at peace with Flask.


回答 2

此代码段:

def decorator(func):
   return func

@decorator
def some_func():
    pass

等效于以下代码:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

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

This code snippet:

def decorator(func):
   return func

@decorator
def some_func():
    pass

Is equivalent to this code:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

In the definition of a decorator you can add some modified things that wouldn’t be returned by a function normally.


回答 3

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

In Python 3.5 you can overload @ as an operator. It is named as __matmul__, because it is designed to do matrix multiplication, but it can be anything you want. See PEP465 for details.

This is a simple implementation of matrix multiplication.

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)

This code yields:

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

回答 4

Python中的“ @”(@)符号有什么作用?

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

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

@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矩阵乘法函数,并且aand 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

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

  • 装饰工
  • 因子之间的运算符
  • 扩充的赋值运算符

装饰语法:

在Google上搜索“ decorator python docs”时,将“ Python语言参考”的“复合语句”部分作为最高结果之一。向下滚动至函数定义部分,我们可以通过搜索“ 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]])

What does the “at” (@) symbol do in Python?

In short, it is used in decorator syntax and for matrix multiplication.

In the context of decorators, this syntax:

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

is equivalent to this:

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

decorated_function = decorator(decorated_function)

In the context of matrix multiplication, a @ b invokes a.__matmul__(b) – making this syntax:

a @ b

equivalent to

dot(a, b)

and

a @= b

equivalent to

a = dot(a, b)

where dot is, for example, the numpy matrix multiplication function and a and b are matrices.

How could you discover this on your own?

I also do not know what to search for as searching Python docs or Google does not return relevant results when the @ symbol is included.

If you want to have a rather complete view of what a particular piece of python syntax does, look directly at the grammar file. For the Python 3 branch:

~$ 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

We can see here that @ is used in three contexts:

  • decorators
  • an operator between factors
  • an augmented assignment operator

Decorator Syntax:

A google search for “decorator python docs” gives as one of the top results, the “Compound Statements” section of the “Python Language Reference.” Scrolling down to the section on function definitions, which we can find by searching for the word, “decorator”, we see that… there’s a lot to read. But the word, “decorator” is a link to the glossary, which tells us:

decorator

A function returning another function, usually applied as a function transformation using the @wrapper syntax. Common examples for decorators are classmethod() and staticmethod().

The decorator syntax is merely syntactic sugar, the following two function definitions are semantically equivalent:

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

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

The same concept exists for classes, but is less commonly used there. See the documentation for function definitions and class definitions for more about decorators.

So, we see that

@foo
def bar():
    pass

is semantically the same as:

def bar():
    pass

bar = foo(bar)

They are not exactly the same because Python evaluates the foo expression (which could be a dotted lookup and a function call) before bar with the decorator (@) syntax, but evaluates the foo expression after bar in the other case.

(If this difference makes a difference in the meaning of your code, you should reconsider what you’re doing with your life, because that would be pathological.)

Stacked Decorators

If we go back to the function definition syntax documentation, we see:

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

is roughly equivalent to

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

This is a demonstration that we can call a function that’s a decorator first, as well as stack decorators. Functions, in Python, are first class objects – which means you can pass a function as an argument to another function, and return functions. Decorators do both of these things.

If we stack decorators, the function, as defined, gets passed first to the decorator immediately above it, then the next, and so on.

That about sums up the usage for @ in the context of decorators.

The Operator, @

In the lexical analysis section of the language reference, we have a section on operators, which includes @, which makes it also an operator:

The following tokens are operators:

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

and in the next page, the Data Model, we have the section Emulating Numeric Types,

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

[…] These methods are called to implement the binary arithmetic operations (+, -, *, @, /, //, […]

And we see that __matmul__ corresponds to @. If we search the documentation for “matmul” we get a link to What’s new in Python 3.5 with “matmul” under a heading “PEP 465 – A dedicated infix operator for matrix multiplication”.

it can be implemented by defining __matmul__(), __rmatmul__(), and __imatmul__() for regular, reflected, and in-place matrix multiplication.

(So now we learn that @= is the in-place version). It further explains:

Matrix multiplication is a notably common operation in many fields of mathematics, science, engineering, and the addition of @ allows writing cleaner code:

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

instead of:

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

While this operator can be overloaded to do almost anything, in numpy, for example, we would use this syntax to calculate the inner and outer product of arrays and matrices:

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

Inplace matrix multiplication: @=

While researching the prior usage, we learn that there is also the inplace matrix multiplication. If we attempt to use it, we may find it is not yet implemented for 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'.

When it is implemented, I would expect the result to look like this:

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

回答 5

Python中的“ @”(@)符号有什么作用?

@符号是python提供的语法糖decorator
以解释这个问题,这恰恰是关于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: '

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

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这是一种花哨且方便的表达方式read_a_book = add_a_book(read_a_book),它是一种语法糖,没有比这更奇特的了。

What does the “at” (@) symbol do in Python?

@ symbol is a syntactic sugar python provides to utilize decorator,
to paraphrase the question, It’s exactly about what does decorator do in Python?

Put it simple decorator allow you to modify a given function’s definition without touch its innermost (it’s closure).
It’s the most case when you import wonderful package from third party. You can visualize it, you can use it, but you cannot touch its innermost and its heart.

Here is a quick example,
suppose I define a read_a_book function on 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: '

You see, I forgot to add a name to it.
How to solve such a problem? Of course, I could re-define the function as:

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

Nevertheless, what if I’m not allowed to manipulate the original function, or if there are thousands of such function to be handled.

Solve the problem by thinking different and define a new_function

def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper

Then employ it.

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, you see, I amended read_a_book without touching it inner closure. Nothing stops me equipped with decorator.

What’s about @

@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 is a fancy and handy way to say read_a_book = add_a_book(read_a_book), it’s a syntactic sugar, there’s nothing more fancier about it.


回答 6

如果您在使用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

If you are referring to some code in a python notebook which is using Numpy library, then @ operator means Matrix Multiplication. For example:

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

回答 7

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

Starting with Python 3.5, the ‘@’ is used as a dedicated infix symbol for MATRIX MULTIPLICATION (PEP 0465 — see https://www.python.org/dev/peps/pep-0465/)


回答 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_方法。

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

@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

最后,让我们看看带有Properties的装饰器。

物产

这些属性提供了一个内置的描述符类型,该描述符类型知道如何将属性链接到一组方法。一个属性带有四个可选参数: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

Decorators were added in Python to make function and method wrapping (a function that receives a function and returns an enhanced one) easier to read and understand. The original use case was to be able to define the methods as class methods or static methods on the head of their definition. Without the decorator syntax, it would require a rather sparse and repetitive definition:

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)

If the decorator syntax is used for the same purpose, the code is shorter and easier to understand:

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

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

General syntax and possible implementations

The decorator is generally a named object ( lambda expressions are not allowed) that accepts a single argument when called (it will be the decorated function) and returns another callable object. “Callable” is used here instead of “function” with premeditation. While decorators are often discussed in the scope of methods and functions, they are not limited to them. In fact, anything that is callable (any object that implements the _call__ method is considered callable), can be used as a decorator and often objects returned by them are not simple functions but more instances of more complex classes implementing their own __call_ method.

The decorator syntax is simply only a syntactic sugar. Consider the following decorator usage:

@some_decorator
def decorated_function():
    pass

This can always be replaced by an explicit decorator call and function reassignment:

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

However, the latter is less readable and also very hard to understand if multiple decorators are used on a single function. Decorators can be used in multiple different ways as shown below:

As a function

There are many ways to write custom decorators, but the simplest way is to write a function that returns a subfunction that wraps the original function call.

The generic patterns is as follows:

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

As a class

While decorators almost always can be implemented using functions, there are some situations when using user-defined classes is a better option. This is often true when the decorator needs complex parametrization or it depends on a specific state.

The generic pattern for a nonparametrized decorator as a class is as follows:

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

Parametrizing decorators

In real code, there is often a need to use decorators that can be parametrized. When the function is used as a decorator, then the solution is simple—a second level of wrapping has to be used. Here is a simple example of the decorator that repeats the execution of a decorated function the specified number of times every time it is called:

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

The decorator defined this way can accept parameters:

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

Note that even if the parametrized decorator has default values for its arguments, the parentheses after its name is required. The correct way to use the preceding decorator with default arguments is as follows:

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

Finally lets see decorators with Properties.

Properties

The properties provide a built-in descriptor type that knows how to link an attribute to a set of methods. A property takes four optional arguments: fget , fset , fdel , and doc . The last one can be provided to define a docstring that is linked to the attribute as if it were a method. Here is an example of a Rectangle class that can be controlled either by direct access to attributes that store two corner points or by using the width , and height properties:

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
    )

The best syntax for creating properties is using property as a decorator. This will reduce the number of method signatures inside of the class and make code more readable and maintainable. With decorators the above class becomes:

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中,就像:

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

这可以用于各种有用的东西,因为功能是对象,而只是指令而已,因此成为可能。

To say what others have in a different way: yes, it is a decorator.

In Python, it’s like:

  1. Creating a function (follows under the @ call)
  2. Calling another function to operate on your created function. This returns a new function. The function that you call is the argument of the @.
  3. Replacing the function defined with the new function returned.

This can be used for all kinds of useful things, made possible because functions are objects and just necessary just instructions.


回答 10

@符号还用于访问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

@ symbol is also used to access variables inside a plydata / pandas dataframe query, pandas.DataFrame.query. Example:

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

回答 11

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

It indicates that you are using a decorator. Here is Bruce Eckel’s example from 2008.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。