Что символ «at» (@) делает в Python?


580

Я смотрю на некоторый код Python, который использовал @символ, но я понятия не имею, что он делает. Я также не знаю, что искать, так как поиск документов Python или Google не возвращает релевантные результаты, когда @символ включен.

Ответы:


305

@Символ в начале строки используется для класса, функций и методов декораторов .

Узнайте больше здесь:

ОПТОСОЗ 318: Декораторы

Python Decorators

Наиболее распространенные декораторы Python, с которыми вы столкнетесь:

@имущество

@classmethod

@staticmethod

Если вы видите @в середине строки, это другое дело, умножение матриц. Прокрутите вниз, чтобы увидеть другие ответы, которые касаются использования @.


31
Похоже, он также может быть оператором матричного умножения: stackoverflow.com/a/21563036/5049813
Pro Q

@decorators также могут быть добавлены
Vijay

349

пример

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сразу после @знака.

Первое наблюдение

Микрофрейм 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.


7
В случае с Flasks app.route("/"): эта функция возвращает функцию, которую вы вызываете с hello()аргументом
shaqed

3
В чем заключается синтаксическое или практическое преимущество наличия декораторов здесь, вместо того, чтобы (например) просто вызывать что-то вроде app.route("/", hello)сразу после определения hello, или даже определять helloкак лямбду в аргументах для app.route? (Последний пример обычен для Node.js http.Serverи Express-маршрутов.)
iono

186

Этот фрагмент кода:

def decorator(func):
   return func

@decorator
def some_func():
    pass

Эквивалентно этому коду:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

В определение декоратора вы можете добавить некоторые измененные вещи, которые не будут возвращены функцией в обычном режиме.


1
В этой строке s "ome_func = decorator (some_func)", первый some_func является переменной = для функции some_func, верно?
Viragos

147

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

14
У вас также есть @=оператор (на месте), который есть __imatmul__.
Пол GD

Есть ли другие переопределяемые операторы, подобные этому? Я знаю __add__и __sub__связан с + и - соответственно, но никогда раньше не слышал о @знаке. Есть ли какие-нибудь другие, скрывающиеся там?
Томас Кимбер

103

Что символ «at» (@) делает в 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, например, функция умножения матриц и aи 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 по запросу «документы-декораторы Python» дает в качестве одного из лучших результатов раздел «Сложные заявления» в «Справочнике по языку Python». Прокрутив вниз до раздела, посвященного определениям функций , который мы можем найти, выполнив поиск по слову «decorator», мы увидим, что ... есть что почитать. Но слово «декоратор» - это ссылка на глоссарий , который говорит нам:

декоратор

Функция, возвращающая другую функцию, обычно применяется как преобразование функции с использованием @wrapperсинтаксиса. Типичными примерами для декораторов являются classmethod()и staticmethod().

Синтаксис декоратора - это просто синтаксический сахар, семантически эквивалентны следующие два определения функций:

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

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

Та же концепция существует для классов, но там используется реже. См. Документацию для определений функций и определений классов для получения дополнительной информации о декораторах.

Итак, мы видим, что

@foo
def bar():
    pass

семантически совпадает с:

def bar():
    pass

bar = foo(bar)

Они не совсем одинаковы, потому что Python оценивает выражение foo (которое может быть точечным поиском и вызовом функции) перед bar с @синтаксисом decorator ( ), но оценивает выражение foo после bar в другом случае.

(Если это различие имеет значение в значении вашего кода, вам следует пересмотреть то, что вы делаете со своей жизнью, потому что это будет патологически.)

С накоплением декораторов

Если мы вернемся к документации по синтаксису определения функции, мы увидим:

@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», мы получаем ссылку на « Что нового в Python 3.5» с «matmul» под заголовком «PEP 465 - Специальный инфиксный оператор для умножения матриц».

он может быть реализован путем определения __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]])

36

Что символ «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: '

Видите ли, я забыл добавить имя к нему.
Как решить такую ​​проблему? Конечно, я мог бы переопределить функцию как:

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

Тем не менее, что делать, если мне не разрешено манипулировать исходной функцией, или если есть тысячи таких функций для обработки.

Решите проблему, думая иначе и определите новую функцию

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_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), что это синтаксический сахар, в этом нет ничего более причудливого.


16

Если вы имеете в виду какой-то код в записной книжке Python, который использует библиотеку Numpy , тогда @ 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


6

В 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")

Общий синтаксис и возможные реализации

Декоратор обычно является именованным объектом ( лямбда-выражения не допускаются ), который принимает один аргумент при вызове (это будет декорированная функция) и возвращает другой вызываемый объект. «Callable» используется здесь вместо «function» с преднамеренностью. Хотя декораторы часто обсуждаются в области методов и функций, они не ограничиваются ими. Фактически, все, что можно вызвать (любой объект, который реализует метод _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

Наконец, давайте посмотрим декораторы со свойствами.

свойства

Свойства предоставляют встроенный тип дескриптора, который знает, как связать атрибут с набором методов. Свойство принимает четыре необязательных аргумента: 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

2

Сказать, что другие имеют по-другому: да, это декоратор.

В Python это похоже на:

  1. Создание функции (следует под @ call)
  2. Вызов другой функции для работы с созданной вами функцией. Это возвращает новую функцию. Вызываемая функция является аргументом @.
  3. Замена функции, определенной новой функцией, возвращается.

Это может использоваться для всех видов полезных вещей, ставших возможными, потому что функции являются объектами и просто необходимы только инструкции.


2

@ Символ также используется для доступа к переменным внутри plydata / панды dataframe запроса pandas.DataFrame.query. Пример:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.