Що робить символ "at" (@) у Python?


579

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

Відповіді:


303

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

Детальніше читайте тут:

PEP 318: декоратори

Декоратори Python

Найпоширеніші декоратори Python:

@ властивість

@classmethod

@staticmethod

Якщо ви бачите @середину рядка, це інша річ, матричне множення. Прокрутіть униз, щоб переглянути інші відповіді, на які використовується адреса @.


31
Схоже, він також може бути оператором множення матриць: stackoverflow.com/a/21563036/5049813
Pro Q

@decorators також можна додати
Vijay Panchal

347

Приклад

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

Усвідомлення цього нарешті дозволило мені відчути спокій з Фляшкою.


7
У випадку Flasks's app.route("/"): ця функція повертає функцію, яку ви викликаєте зі своїм hello()аргументом
shaqed

3
Яка синтаксична чи практична користь мати тут декораторів, а не (наприклад) просто називати щось на кшталт app.route("/", hello)одразу після визначення helloабо навіть визначати helloяк лямбда в аргументах app.route? (Останній приклад є загальним з Node.js http.Serverі експрес - маршрутів.)
IONO

185

Цей фрагмент коду:

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 = декоратор (some_func)", перший some_func є змінною = до функції some_func, правильно?
Вірагос

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__.
Pål 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". Прокручуючи вниз до розділу про визначення функцій , які ми можемо знайти, шукаючи слово «декоратор», ми бачимо, що ... є що читати. Але слово "декоратор" - це посилання на словник , який говорить нам:

декоратор

Функція, що повертає іншу функцію, зазвичай застосовується як перетворення функції за допомогою @wrapperсинтаксису. Поширеними прикладами для декораторів є classmethod()і staticmethod().

Синтаксис декоратора - це просто синтаксичний цукор, наступні два визначення функції є семантично еквівалентними:

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

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

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

Отже, ми це бачимо

@foo
def bar():
    pass

семантично те саме, що:

def bar():
    pass

bar = foo(bar)

Вони не зовсім однакові, тому що Python оцінює вираз foo (який може бути пунктирним пошуком і викликом функції) перед рядком із @синтаксисом decorator ( ), але в іншому випадку оцінює вираз 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", ми отримаємо посилання на Що нового в Python 3.5 з "matmul" під заголовком "PEP 465 - виділений оператор інфікування для множення матриці".

він може бути реалізований шляхом визначення __matmul__(), __rmatmul__()а також __imatmul__()для регулярного, відображеного та на місці матричного множення.

(Отже, тепер ми дізнаємось, що @=це in-place версія). Далі це пояснює:

Матричне множення є особливо поширеною операцією в багатьох галузях математики, науки, техніки, а додавання @ дозволяє писати чистіший код:

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

Якщо ви маєте на увазі якийсь - то код в пітона ноутбук , який використовує Numpy бібліотеку, то @ 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


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. Останній може бути наданий для визначення docstring, який пов'язаний з атрибутом так, ніби це метод. Ось приклад класу прямокутника, яким можна керувати або прямим доступом до атрибутів, які зберігають дві кутові точки, або за допомогою властивостей ширини та висоти:

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.