Відповіді:
@
Символ на початку рядка використовуються для класу, функцій і методів декораторів .
Детальніше читайте тут:
Найпоширеніші декоратори Python:
Якщо ви бачите @
середину рядка, це інша річ, матричне множення. Прокрутіть униз, щоб переглянути інші відповіді, на які використовується адреса @
.
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
Усвідомлення цього нарешті дозволило мені відчути спокій з Фляшкою.
app.route("/")
: ця функція повертає функцію, яку ви викликаєте зі своїм hello()
аргументом
app.route("/", hello)
одразу після визначення hello
або навіть визначати hello
як лямбда в аргументах app.route
? (Останній приклад є загальним з Node.js http.Server
і експрес - маршрутів.)
Цей фрагмент коду:
def decorator(func):
return func
@decorator
def some_func():
pass
Еквівалентний цьому коду:
def decorator(func):
return func
def some_func():
pass
some_func = decorator(some_func)
У визначення декоратора ви можете додати деякі модифіковані речі, які функція не повертається нормально.
У 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]]
@=
(на місці) оператор, який є __imatmul__
.
__add__
і __sub__
пов’язані відповідно з + і - відповідно, але ніколи не чув про @
знаковий. Чи хтось там ховається?
Коротше кажучи, він використовується в синтаксисі декоратора та для матричного множення.
У контексті декораторів цей синтаксис:
@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]])
Що робить символ "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)
, що це синтаксичний цукор, в цьому немає нічого більш фантазійного.
Якщо ви маєте на увазі якийсь - то код в пітона ноутбук , який використовує 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
Починаючи з Python 3.5, "@" використовується як виділений символ інфіксації для MATRIX MULTIPLICATION (PEP 0465 - див. Https://www.python.org/dev/peps/pep-0465/ )
Декоратори були додані в 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
Сказати те, що у інших є по-іншому: так, це декоратор.
У Python це так:
Це може бути використано для всіляких корисних речей, що стало можливим, оскільки функції - це об'єкти і потрібні лише інструкції.
Це означає, що ви використовуєте декоратор. Ось приклад Брюса Еккеля з 2008 року.