Jawaban:
Sebuah @
simbol pada awal baris digunakan untuk kelas, fungsi dan metode dekorator .
Baca lebih lanjut di sini:
Dekorator Python paling umum yang akan Anda temui adalah:
Jika Anda melihat sebuah @
di tengah garis, itu hal yang berbeda, perkalian matriks. Gulir ke bawah untuk melihat jawaban lain yang membahas tentang penggunaan @
.
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']
Ini menunjukkan bahwa function
/ method
/ class
Anda mendefinisikan setelah dekorator hanya pada dasarnya diteruskan sebagai argument
ke function
/ method
segera setelah @
tanda.
The microframework Flask memperkenalkan dekorator dari awal dalam format berikut:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
Ini pada gilirannya berarti:
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
Menyadari hal ini akhirnya membuat saya merasa damai dengan Flask.
app.route("/")
: fungsi ini mengembalikan fungsi, yang Anda menjalankan dengan Anda hello()
sebagai argumen
app.route("/", hello)
segera setelah mendefinisikan hello
, atau bahkan mendefinisikan hello
sebagai lambda dalam argumen app.route
? (Contoh terakhir adalah umum dengan Node.js http.Server
dan rute Express.)
Cuplikan kode ini:
def decorator(func):
return func
@decorator
def some_func():
pass
Setara dengan kode ini:
def decorator(func):
return func
def some_func():
pass
some_func = decorator(some_func)
Dalam definisi dekorator Anda dapat menambahkan beberapa hal yang dimodifikasi yang tidak akan dikembalikan oleh fungsi secara normal.
Dalam Python 3.5 Anda dapat membebani @
sebagai operator. Disebut sebagai __matmul__
, karena dirancang untuk melakukan perkalian matriks, tetapi bisa berupa apa saja yang Anda inginkan. Lihat PEP465 untuk detailnya.
Ini adalah implementasi sederhana dari perkalian matriks.
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)
Kode ini menghasilkan:
[[18, 14], [62, 66]]
@=
operator (di tempat), yaitu __imatmul__
.
__add__
dan __sub__
terhubung ke + dan - masing-masing, tetapi tidak pernah mendengar @
tanda itu sebelumnya. Apakah ada orang lain yang bersembunyi di luar sana?
Singkatnya, ini digunakan dalam sintaksis dekorator dan untuk perkalian matriks.
Dalam konteks dekorator, sintaks ini:
@decorator
def decorated_function():
"""this function is decorated"""
setara dengan ini:
def decorated_function():
"""this function is decorated"""
decorated_function = decorator(decorated_function)
Dalam konteks perkalian matriks, a @ b
aktifkan a.__matmul__(b)
- buat sintaks ini:
a @ b
setara dengan
dot(a, b)
dan
a @= b
setara dengan
a = dot(a, b)
di mana dot
, misalnya, fungsi perkalian matriks numpy dan a
dan b
adalah matriks.
Saya juga tidak tahu apa yang harus dicari sebagai pencarian Python docs atau Google tidak memberikan hasil yang relevan ketika simbol @ disertakan.
Jika Anda ingin memiliki pandangan yang agak lengkap tentang apa yang dilakukan sintaks python tertentu, lihat langsung pada file grammar. Untuk cabang 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
Kita dapat melihat di sini yang @
digunakan dalam tiga konteks:
Pencarian google untuk "dekorator python docs" memberikan sebagai salah satu hasil teratas, bagian "Pernyataan Majemuk" dari "Referensi Bahasa Python." Menggulir ke bawah ke bagian definisi fungsi , yang dapat kita temukan dengan mencari kata, "dekorator", kita melihat bahwa ... ada banyak yang harus dibaca. Tetapi kata, "dekorator" adalah tautan ke glosarium , yang memberi tahu kita:
penghias
Fungsi yang mengembalikan fungsi lain, biasanya diterapkan sebagai transformasi fungsi menggunakan
@wrapper
sintaks. Contoh umum untuk dekorator adalahclassmethod()
danstaticmethod()
.Sintaksis dekorator hanyalah gula sintaksis, dua definisi fungsi berikut ini secara semantik setara:
def f(...): ... f = staticmethod(f) @staticmethod def f(...): ...
Konsep yang sama ada untuk kelas, tetapi lebih jarang digunakan di sana. Lihat dokumentasi untuk definisi fungsi dan definisi kelas untuk informasi lebih lanjut tentang dekorator.
Jadi, kita lihat itu
@foo
def bar():
pass
secara semantik sama dengan:
def bar():
pass
bar = foo(bar)
Mereka tidak persis sama karena Python mengevaluasi ekspresi foo (yang bisa berupa pencarian titik-titik dan panggilan fungsi) sebelum bilah dengan @
sintaks dekorator ( ), tetapi mengevaluasi ekspresi foo setelah bilah dalam kasus lain.
(Jika perbedaan ini membuat perbedaan dalam arti kode Anda, Anda harus mempertimbangkan kembali apa yang Anda lakukan dengan hidup Anda, karena itu akan menjadi patologis.)
Jika kita kembali ke dokumentasi sintaks definisi fungsi, kita melihat:
@f1(arg) @f2 def func(): pass
kira-kira setara dengan
def func(): pass func = f1(arg)(f2(func))
Ini adalah demonstrasi yang dapat kita sebut fungsi yang merupakan dekorator terlebih dahulu, serta dekorator tumpukan. Functions, dalam Python, adalah objek kelas satu - yang berarti Anda bisa melewatkan fungsi sebagai argumen ke fungsi lain, dan mengembalikan fungsi. Dekorator melakukan kedua hal ini.
Jika kita menumpuk dekorator, fungsinya, seperti yang didefinisikan, akan diteruskan terlebih dahulu ke dekorator tepat di atasnya, lalu berikutnya, dan seterusnya.
Itu tentang merangkum penggunaan @
dalam konteks dekorator.
@
Di bagian analisis leksikal dari referensi bahasa, kami memiliki bagian tentang operator , yang termasuk @
, yang menjadikannya juga operator:
Token berikut adalah operator:
+ - * ** / // % @ << >> & | ^ ~ < > <= >= == !=
dan di halaman berikutnya, Model Data, kita memiliki bagian Mengemulasi Jenis Numerik ,
object.__add__(self, other) object.__sub__(self, other) object.__mul__(self, other) object.__matmul__(self, other) object.__truediv__(self, other) object.__floordiv__(self, other)
[...] Metode ini dipanggil untuk melaksanakan operasi aritmatika biner (
+
,-
,*
,@
,/
,//
, [...]
Dan kita melihat itu __matmul__
sesuai dengan @
. Jika kita mencari dokumentasi untuk "matmul" kita mendapatkan tautan ke Apa yang baru di Python 3.5 dengan "matmul" di bawah judul "PEP 465 - Operator infiks khusus untuk perkalian matriks".
itu dapat diimplementasikan dengan mendefinisikan
__matmul__()
,,__rmatmul__()
dan__imatmul__()
untuk perkalian matriks reguler, tercermin, dan di tempat.
(Jadi sekarang kita belajar bahwa itu @=
adalah versi di tempat). Lebih lanjut dijelaskan:
Perkalian matriks adalah operasi yang sangat umum di banyak bidang matematika, sains, teknik, dan penambahan @ memungkinkan penulisan kode pembersih:
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)
dari pada:
S = dot((dot(H, beta) - r).T, dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))
Meskipun operator ini dapat kelebihan beban untuk melakukan hampir semua hal, numpy
misalnya, kami akan menggunakan sintaks ini untuk menghitung produk dalam dan luar dari susunan dan matriks:
>>> 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]])
@=
Saat meneliti penggunaan sebelumnya, kami belajar bahwa ada juga perkalian matriks inplace. Jika kami mencoba menggunakannya, kami mungkin menemukan itu belum diterapkan untuk 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'.
Ketika diimplementasikan, saya berharap hasilnya akan terlihat seperti ini:
>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])
Apa yang dilakukan simbol "at" (@) dengan Python?
Simbol @ adalah python gula sintaksis menyediakan untuk memanfaatkan decorator
,
untuk parafrase pertanyaan, Ini persis tentang apa yang dekorator lakukan di Python?
Sederhananya decorator
memungkinkan Anda untuk mengubah definisi fungsi yang diberikan tanpa menyentuh yang paling dalam (itu penutupan).
Ini adalah kasus terbanyak ketika Anda mengimpor paket luar biasa dari pihak ketiga. Anda dapat memvisualisasikannya, Anda dapat menggunakannya, tetapi Anda tidak dapat menyentuh bagian terdalam dan hatinya.
Berikut ini adalah contoh cepat,
misalkan saya mendefinisikan read_a_book
fungsi di 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: '
Anda lihat, saya lupa menambahkan nama untuk itu.
Bagaimana mengatasi masalah seperti itu? Tentu saja, saya dapat mendefinisikan kembali fungsi sebagai:
def read_a_book():
return "I am reading the book: 'Python Cookbook'"
Namun demikian, bagaimana jika saya tidak diizinkan untuk memanipulasi fungsi asli, atau jika ada ribuan fungsi yang harus ditangani.
Selesaikan masalah dengan berpikir berbeda dan tentukan fungsi baru
def add_a_book(func):
def wrapper():
return func() + "Python Cookbook"
return wrapper
Kemudian gunakan itu.
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, Anda tahu, saya diam read_a_book
tanpa menyentuhnya dengan penutupan batin. Tidak ada yang menghentikan saya dilengkapi decorator
.
Tentang apa @
@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
adalah cara yang mewah dan praktis untuk mengatakan read_a_book = add_a_book(read_a_book)
, ini adalah gula sintaksis, tidak ada yang lebih menarik dari itu.
Jika Anda merujuk pada beberapa kode dalam buku catatan python yang menggunakan Numpy library, maka @ operator
artinya Penggandaan Matriks . Sebagai contoh:
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
Dimulai dengan Python 3.5, '@' digunakan sebagai simbol infiks khusus untuk MATRIX MULTIPLICATION (PEP 0465 - lihat https://www.python.org/dev/peps/pep-0465/ )
Dekorator ditambahkan dengan Python untuk membuat pembungkus fungsi dan metode (fungsi yang menerima fungsi dan mengembalikan fungsi yang ditingkatkan) lebih mudah dibaca dan dipahami. Kasus penggunaan asli adalah untuk dapat mendefinisikan metode sebagai metode kelas atau metode statis di kepala definisi mereka. Tanpa sintaksis dekorator, diperlukan definisi yang agak jarang dan berulang:
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)
Jika sintaksis dekorator digunakan untuk tujuan yang sama, kode lebih pendek dan lebih mudah dipahami:
class WithDecorators:
@staticmethod
def some_static_method():
print("this is static method")
@classmethod
def some_class_method(cls):
print("this is class method")
Sintaksis umum dan kemungkinan implementasi
Dekorator umumnya adalah objek bernama ( ekspresi lambda tidak diizinkan ) yang menerima argumen tunggal ketika dipanggil (itu akan menjadi fungsi yang didekorasi) dan mengembalikan objek yang dapat dipanggil lainnya. "Callable" digunakan di sini alih-alih "berfungsi" dengan perencanaan terlebih dahulu. Sementara dekorator sering dibahas dalam ruang lingkup metode dan fungsi, mereka tidak terbatas pada mereka. Bahkan, apa pun yang dapat dipanggil (objek apa pun yang mengimplementasikan metode _call__ dianggap callable), dapat digunakan sebagai dekorator dan seringkali objek yang dikembalikan oleh mereka bukanlah fungsi sederhana tetapi lebih banyak contoh kelas yang lebih kompleks yang menerapkan metode __call_ mereka sendiri.
Sintaksis dekorator hanyalah gula sintaksis . Pertimbangkan penggunaan dekorator berikut:
@some_decorator
def decorated_function():
pass
Ini selalu dapat digantikan oleh panggilan dekorator dan pengalihan fungsi secara eksplisit:
def decorated_function():
pass
decorated_function = some_decorator(decorated_function)
Namun, yang terakhir kurang dapat dibaca dan juga sangat sulit untuk dipahami jika beberapa dekorator digunakan pada satu fungsi. Dekorator dapat digunakan dalam berbagai cara seperti yang ditunjukkan di bawah ini:
Sebagai fungsi
Ada banyak cara untuk menulis dekorator khusus, tetapi cara paling sederhana adalah menulis fungsi yang mengembalikan subfungsi yang membungkus panggilan fungsi asli.
Pola generik adalah sebagai berikut:
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
Sebagai kelas
Sementara dekorator hampir selalu dapat diimplementasikan menggunakan fungsi, ada beberapa situasi ketika menggunakan kelas yang ditentukan pengguna adalah pilihan yang lebih baik. Ini sering benar ketika dekorator membutuhkan parametrization yang kompleks atau tergantung pada keadaan tertentu.
Pola umum untuk dekorator nonparametrized sebagai kelas adalah sebagai berikut:
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
Dekorator parametrizing
Dalam kode sebenarnya, sering kali ada kebutuhan untuk menggunakan dekorator yang dapat parametrized. Ketika fungsi tersebut digunakan sebagai dekorator, maka solusinya sederhana — pembungkus tingkat kedua harus digunakan. Berikut ini adalah contoh sederhana dari dekorator yang mengulangi pelaksanaan fungsi yang didekorasi berapa kali setiap kali dipanggil:
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
Dekorator yang ditentukan dengan cara ini dapat menerima parameter:
>>> @repeat(2)
... def foo():
... print("foo")
...
>>> foo()
foo
foo
Perhatikan bahwa bahkan jika dekorator parametrized memiliki nilai default untuk argumennya, tanda kurung setelah namanya diperlukan. Cara yang benar untuk menggunakan dekorator sebelumnya dengan argumen default adalah sebagai berikut:
>>> @repeat()
... def bar():
... print("bar")
...
>>> bar()
bar
bar
bar
Akhirnya mari kita lihat dekorator dengan Properties.
Properti
Properti menyediakan tipe deskriptor bawaan yang tahu cara menautkan atribut ke serangkaian metode. Properti mengambil empat argumen opsional: fget, fset, fdel, dan doc. Yang terakhir dapat disediakan untuk mendefinisikan docstring yang dihubungkan dengan atribut seolah-olah itu adalah metode. Berikut adalah contoh kelas Rectangle yang dapat dikontrol baik dengan akses langsung ke atribut yang menyimpan dua titik sudut atau dengan menggunakan properti width, dan 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
)
Sintaks terbaik untuk membuat properti menggunakan properti sebagai dekorator. Ini akan mengurangi jumlah tanda tangan metode di dalam kelas dan membuat kode lebih mudah dibaca dan dipelihara . Dengan dekorator, kelas di atas menjadi:
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
Untuk mengatakan apa yang orang lain miliki dengan cara yang berbeda: ya, itu adalah dekorator.
Dalam Python, itu seperti:
Ini dapat digunakan untuk semua jenis hal yang bermanfaat, dimungkinkan karena fungsi adalah objek dan hanya diperlukan instruksi saja.
Ini menunjukkan bahwa Anda menggunakan dekorator. Ini adalah contoh Bruce Eckel dari 2008.