Ответы:
@
Символ в начале строки используется для класса, функций и методов декораторов .
Узнайте больше здесь:
Наиболее распространенные декораторы 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
сразу после @
знака.
Микрофрейм 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.
app.route("/")
: эта функция возвращает функцию, которую вы вызываете с hello()
аргументом
app.route("/", hello)
сразу после определения hello
, или даже определять hello
как лямбду в аргументах для app.route
? (Последний пример обычен для Node.js http.Server
и Express-маршрутов.)
Этот фрагмент кода:
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». Прокрутив вниз до раздела, посвященного определениям функций , который мы можем найти, выполнив поиск по слову «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]])
Что символ «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)
, что это синтаксический сахар, в этом нет ничего более причудливого.
Если вы имеете в виду какой-то код в записной книжке 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
Начиная с 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. Последний может быть предоставлен для определения строки документации, которая связана с атрибутом, как если бы это был метод. Вот пример класса 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
Сказать, что другие имеют по-другому: да, это декоратор.
В Python это похоже на:
Это может использоваться для всех видов полезных вещей, ставших возможными, потому что функции являются объектами и просто необходимы только инструкции.
Это указывает на то, что вы используете декоратор. Вот пример Брюса Экеля из 2008 года.