Apa yang dilakukan simbol "at" (@) dengan Python?

580

Saya melihat beberapa kode Python yang menggunakan @simbol, tapi saya tidak tahu apa fungsinya. Saya juga tidak tahu apa yang harus dicari sebagai pencarian Python docs atau Google tidak memberikan hasil yang relevan ketika @simbol disertakan.

AJ00200
sumber

Jawaban:

305

Sebuah @simbol pada awal baris digunakan untuk kelas, fungsi dan metode dekorator .

Baca lebih lanjut di sini:

PEP 318: Dekorator

Dekorator Python

Dekorator Python paling umum yang akan Anda temui adalah:

@Properti

@classmethod

@staticmethod

Jika Anda melihat sebuah @di tengah garis, itu hal yang berbeda, perkalian matriks. Gulir ke bawah untuk melihat jawaban lain yang membahas tentang penggunaan @.

FogleBird
sumber
31
Sepertinya juga bisa menjadi operator perkalian matriks juga: stackoverflow.com/a/21563036/5049813
Pro Q
@decorators juga dapat ditambahkan
Vijay Panchal
349

Contoh

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/ classAnda mendefinisikan setelah dekorator hanya pada dasarnya diteruskan sebagai argumentke function/ methodsegera setelah @tanda.

Penampakan pertama

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.

Morgan Wilde
sumber
7
Dalam Labu kasus ini app.route("/"): fungsi ini mengembalikan fungsi, yang Anda menjalankan dengan Anda hello()sebagai argumen
shaqed
3
Apa manfaat sintaksis atau praktis dari memiliki dekorator di sini, alih-alih (misalnya) hanya memanggil sesuatu seperti app.route("/", hello)segera setelah mendefinisikan hello, atau bahkan mendefinisikan hellosebagai lambda dalam argumen app.route? (Contoh terakhir adalah umum dengan Node.js http.Serverdan rute Express.)
iono
186

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.

Matheus Araujo
sumber
1
Pada baris ini s "ome_func = dekorator (some_func)", some_func pertama adalah variabel = ke fungsi some_func, benar?
Viragos
147

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]]
jinhwanlazy
sumber
14
Anda juga memiliki @=operator (di tempat), yaitu __imatmul__.
Pål GD
Apakah ada operator lain yang bisa ditimpa seperti ini? Saya tahu __add__dan __sub__terhubung ke + dan - masing-masing, tetapi tidak pernah mendengar @tanda itu sebelumnya. Apakah ada orang lain yang bersembunyi di luar sana?
Thomas Kimber
103

Apa yang dilakukan simbol "at" (@) dengan Python?

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 @ baktifkan 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 adan badalah matriks.

Bagaimana Anda bisa menemukan ini sendiri?

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:

  • dekorator
  • operator antara faktor
  • operator penugasan augmented

Sintaks dekorator:

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 @wrappersintaks. Contoh umum untuk dekorator adalah classmethod()dan staticmethod().

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.)

Dekorator Tertumpuk

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.

Operator, @

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, numpymisalnya, 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]])

Multiplikasi matriks inplace: @=

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]])
Aaron Hall
sumber
36

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 decoratormemungkinkan 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_bookfungsi 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_booktanpa 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_bookadalah 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.

Kalkulus
sumber
16

Jika Anda merujuk pada beberapa kode dalam buku catatan python yang menggunakan Numpy library, maka @ operatorartinya 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
f__society
sumber
6

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
iun1x
sumber
2

Untuk mengatakan apa yang orang lain miliki dengan cara yang berbeda: ya, itu adalah dekorator.

Dalam Python, itu seperti:

  1. Membuat fungsi (ikuti di bawah panggilan @)
  2. Memanggil fungsi lain untuk beroperasi pada fungsi yang Anda buat. Ini mengembalikan fungsi baru. Fungsi yang Anda panggil adalah argumen dari @.
  3. Mengganti fungsi yang ditentukan dengan fungsi yang baru dikembalikan.

Ini dapat digunakan untuk semua jenis hal yang bermanfaat, dimungkinkan karena fungsi adalah objek dan hanya diperlukan instruksi saja.

Mayur Patel
sumber
2

@ Simbol ini juga digunakan untuk variabel akses di dalam plydata / panda dataframe query, pandas.DataFrame.query. Contoh:

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

Ini menunjukkan bahwa Anda menggunakan dekorator. Ini adalah contoh Bruce Eckel dari 2008.

Peter Rowell
sumber