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

2018-12-31 13:07发布

问题:

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.

回答1:

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



回答2:

Preamble

I admit it took more than a few moments to fully grasp this concept for me, so I\'ll share what I\'ve learned to save others the trouble.

The name decorator - the thing we define using the @ syntax before a function definition - was probably the main culprit here.

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.



回答3:

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.



回答4:

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


回答5:

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


回答6:

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.



回答7:

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:

@ 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


回答9:

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



回答10:

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.



回答11:

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