Python'da “at” (@) sembolü ne işe yarar?


579

@Sembolü kullanan bazı Python kodlarına bakıyorum , ama ne yaptığı hakkında hiçbir fikrim yok. Ayrıca Python belgelerinde arama yaparken neyi arayacağımı da bilmiyorum veya @sembol dahil edildiğinde Google ilgili sonuçları döndürmüyor .

Yanıtlar:


303

@Sınıf, işlev ve yöntem dekoratörleri için bir satırın başındaki bir sembol kullanılır .

Daha fazlasını buradan okuyun:

PEP 318: Dekoratörler

Python Dekoratörleri

Karşılaşacağınız en yaygın Python dekoratörleri şunlardır:

@Emlak

@classmethod

@staticmethod

@Bir satırın ortasında bir görürseniz , bu farklı bir şeydir, matris çarpımı. Kullanımıyla ilgili diğer yanıtları görmek için aşağı kaydırın @.


31
Aynı zamanda bir matris çarpma operatörü de olabilir: stackoverflow.com/a/21563036/5049813
Pro Q

@decorators da eklenebilir
Vijay

347

Misal

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']

O Bu gösterileri function/ method/ classa sonra tanımlarken dekoratör sadece temelde bir şekilde geçirilir argumentiçin function/ methodhemen sonra @işareti.

İlk nişan

Mikro çerçeve Flask , en başından dekoratörleri aşağıdaki formatta tanıtmaktadır :

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

Bu da şu anlama gelir:

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

Bunun farkına varmak nihayet Flask ile barış içinde hissetmemi sağladı.


7
Flasks's durumunda app.route("/"): Bu işlev, sizinle hello()argüman olarak çağırdığınız bir işlevi döndürür
shaqed

3
Burada dekoratörlere sahip olmanın sözdizimsel ya da pratik yararı ne, mesela sadece argümanları app.route("/", hello)tanımladıktan hemen sonra çağırmak hello, hatta helloargümanlarda lambda olarak tanımlamak yerine app.route? (İkinci örnek Node.js http.Serverve Express rotalarında yaygındır .)
iono

185

Bu kod snippet'i:

def decorator(func):
   return func

@decorator
def some_func():
    pass

Bu koda eşdeğerdir:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

Bir dekoratör tanımında, bir işlev tarafından normal olarak döndürülmeyecek bazı değiştirilmiş şeyler ekleyebilirsiniz.


1
Bu satırda "ome_func = dekoratör (some_func)", ilk some_func bir değişken = işlevi some_func, doğru mu?
Viragos

147

Python 3.5'te @bir operatör olarak aşırı yükleyebilirsiniz . Olarak adlandırılır __matmul__, çünkü matris çarpımı yapmak için tasarlanmıştır, ancak istediğiniz herhangi bir şey olabilir. Ayrıntılar için PEP465'e bakın.

Bu, matris çarpımının basit bir uygulamasıdır.

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)

Bu kod şunları sağlar:

[[18, 14], [62, 66]]

14
Ayrıca @=(yerinde) operatörünüz de vardır __imatmul__.
Pål GD

Bunun gibi geçersiz kılınan başka operatörler var mı? Bildiğim __add__ve __sub__+ 'ya bağlı olması ve - sırasıyla ancak duymadım @önce işaret biri. Orada gizlenen başkaları var mı?
Thomas Kimber

103

Python'da “at” (@) sembolü ne işe yarar?

Kısacası, dekoratör sözdiziminde ve matris çarpımında kullanılır.

Dekoratörler bağlamında, bu sözdizimi:

@decorator
def decorated_function():
    """this function is decorated"""

buna eşdeğerdir:

def decorated_function():
    """this function is decorated"""

decorated_function = decorator(decorated_function)

Matris çarpımı bağlamında, a @ bçağırır a.__matmul__(b)- bu sözdizimini yapmak:

a @ b

eşittir

dot(a, b)

ve

a @= b

eşittir

a = dot(a, b)

burada dot, numpy matris çarpma fonksiyonu ve ave bmatrislerdir.

Bunu kendi başınıza nasıl keşfedebilirsiniz?

Ayrıca Python belgelerinde arama yaparken neyi arayacağımı da bilmiyorum veya @ sembolü dahil edildiğinde Google alakalı sonuçlar döndürmüyor.

Belirli bir python sözdizimi parçasının ne yaptığına dair tam bir görünüm elde etmek istiyorsanız, doğrudan dilbilgisi dosyasına bakın. Python 3 dalı için:

~$ 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

Burada @üç bağlamda kullanılanları görebiliriz :

  • dekoratörler
  • faktörler arasında bir operatör
  • artırılmış bir atama operatörü

Dekoratör Sözdizimi:

"Dekoratör python dokümanları" için yapılan bir google araması, "Python Dil Referansı" nın "Bileşik İfadeleri" bölümünde en iyi sonuçlardan biri olarak verilir. "Dekoratör" kelimesini arayarak bulabileceğimiz işlev tanımları bölümüne doğru ilerlerken şunu görüyoruz ... okunacak çok şey var. Ancak "dekoratör" kelimesi, sözlüğe bir bağlantıdır ve bize şunu söyler:

dekoratör

Genellikle @wrappersözdizimi kullanılarak işlev dönüşümü olarak uygulanan başka bir işlev döndüren işlev . Dekoratörler için ortak örnekler classmethod()ve staticmethod().

Dekoratör sözdizimi sadece sözdizimsel şekerdir, aşağıdaki iki işlev tanımı anlamsal olarak eşdeğerdir:

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

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

Aynı kavram sınıflar için de mevcuttur, ancak orada daha az kullanılır. Dekoratörler hakkında daha fazla bilgi için işlev tanımları ve sınıf tanımları belgelerine bakın.

Görüyoruz ki

@foo
def bar():
    pass

anlamsal olarak aynıdır:

def bar():
    pass

bar = foo(bar)

Tam olarak aynı değildir, çünkü Python, dekoratör ( @) sözdizimiyle çubuktan önce foo ifadesini (noktalı bir arama ve bir işlev çağrısı olabilir) değerlendirir, ancak diğer durumda çubuktan sonra foo ifadesini değerlendirir .

(Bu fark kodunuzun anlamında bir fark yaratırsa, hayatınızla ne yaptığınızı yeniden düşünmelisiniz, çünkü bu patolojik olacaktır.)

Yığılmış Dekoratörler

İşlev tanımı sözdizimi belgelerine dönersek, şunu görürüz:

@f1(arg)
@f2
def func(): pass

kabaca eşdeğer

def func(): pass
func = f1(arg)(f2(func))

Bu, ilk olarak dekoratör ve yığın dekoratörler olarak adlandırılan bir işlevi çağırabileceğimizin bir göstergesidir. Python'daki işlevler birinci sınıf nesnelerdir; başka bir deyişle, bir işlevi başka bir işleve bağımsız değişken olarak iletebilir ve işlevleri döndürebilirsiniz. Dekoratörler her ikisini de yapar.

Dekoratörleri yığınlarsak, tanımlandığı gibi işlev önce dekoratörün hemen üstüne, ardından bir sonraki dekora aktarılır.

Bu @dekoratörler bağlamında kullanımını özetlemektedir .

Operatör, @

Dil referansının sözcüksel analiz bölümünde , operatörler hakkında bir operatör var , bu bölüm@ de bir operatör yapıyor:

Aşağıdaki simgeler işleçlerdir:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=

ve bir sonraki sayfada, Veri Modeli'nde Sayısal Tipleri Taklit Etme ,

object.__add__(self, other)
object.__sub__(self, other) 
object.__mul__(self, other) 
object.__matmul__(self, other) 
object.__truediv__(self, other) 
object.__floordiv__(self, other)

[...] Bu yöntemler (ikili aritmetik işlemleri uygulamak için denir +, -, *, @, /, //, [...]

Ve __matmul__buna karşılık geldiğini görüyoruz @. Eğer "matmul" belgelerinde arama yaparsak, "PEP 465 - Matris çarpımı için özel bir infix operatörü" başlığı altında "matmul" ile Python 3.5'teki Yenilikler bağlantısını alırız.

düzenli, yansıyan ve yerinde matris çarpımı tanımlanarak __matmul__(), __rmatmul__()ve uygulanabilir __imatmul__().

(Şimdi bunun @=yerinde sürüm olduğunu öğreniyoruz ). Ayrıca şunları açıklar:

Matris çarpımı, matematik, fen, mühendislik alanlarında birçok alanda oldukça yaygın bir işlemdir ve @ eklenmesi daha temiz kod yazılmasına izin verir:

S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

onun yerine:

S = dot((dot(H, beta) - r).T,
        dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))

Bu operatör neredeyse her şeyi yapmak için aşırı yüklenebilirken numpy, örneğin, dizilerin ve matrislerin iç ve dış ürünlerini hesaplamak için bu sözdizimini kullanırız:

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

Yerinde matris çarpımı: @=

Önceki kullanımı araştırırken, yerinde matris çarpımının da olduğunu öğreniyoruz. Kullanmaya çalışırsak, henüz numpy için uygulanmadığını görebiliriz:

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

Uygulandığında, sonucun şöyle görünmesini beklerim:

>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])

36

Python'da “at” (@) sembolü ne işe yarar?

@ sembolü sözdizimsel bir şeker python kullanmak decorator,
soruyu yorumlamak için sağlar, Tam olarak dekoratör Python ne yapar hakkında?

Basitçe söylemek decoratorgerekirse, belirli bir işlevin tanımını en içteki değerine (kapatmadan) değiştirmeden değiştirmenize izin verir.
Üçüncü taraftan harika bir paket aldığınızda en çok durumdur. Görselleştirebilir, kullanabilirsiniz, ancak en iç kısmına ve kalbine dokunamazsınız.

İşte hızlı bir örnek,
varsayalım read_a_bookIpython üzerinde bir işlev tanımlamak

In [9]: def read_a_book():
   ...:     return "I am reading the book: "
   ...: 
In [10]: read_a_book()
Out[10]: 'I am reading the book: '

Bakın, ona bir isim eklemeyi unuttum.
Böyle bir problem nasıl çözülür? Tabii ki, fonksiyonu yeniden tanımlayabilirim:

def read_a_book():
    return "I am reading the book: 'Python Cookbook'"

Yine de, orijinal işlevi değiştirmeme izin verilmezse veya ele alınacak binlerce işlev varsa.

Sorunu farklı düşünerek çözün ve yeni bir işlev tanımlayın

def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper

Sonra işe alın.

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, görüyorsun, read_a_bookiç kapamaya dokunmadan değiştirdim . Hiçbir şey donanıma engel olmaz decorator.

Ne hakkında @

@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_booksöylemenin süslü ve kullanışlı bir yoludur read_a_book = add_a_book(read_a_book), sözdizimsel bir şekerdir, bu konuda daha meraklı bir şey yoktur.


16

Numpy kitaplığını kullanan bir python not defterinde bazı kodlara başvuruyorsanız , Matrix Multiplication@ operator anlamına gelir . Örneğin:

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

Dekoratörler, işlev ve yöntem kaydırmayı (bir işlevi alan ve gelişmiş bir işlevi döndüren bir işlev) okumayı ve anlamayı kolaylaştırmak için Python'a eklenmiştir . Orijinal kullanım durumu, yöntemleri tanımlarının başında sınıf yöntemleri veya statik yöntemler olarak tanımlayabilmekteydi. Dekoratör sözdizimi olmadan, oldukça seyrek ve tekrarlayan bir tanım gerektirir:

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)

Dekoratör sözdizimi aynı amaçla kullanılırsa, kod daha kısa ve anlaşılması daha kolaydır:

class WithDecorators:
    @staticmethod
    def some_static_method():
        print("this is static method")

    @classmethod
    def some_class_method(cls):
        print("this is class method")

Genel sözdizimi ve olası uygulamalar

Dekoratör genellikle adlandırılmış bir nesnedir ( lambda ifadelerine izin verilmez ) çağrıldığında tek bir argümanı kabul eder (dekore edilmiş işlev olacaktır) ve başka bir çağrılabilir nesne döndürür. Burada premeditasyon ile "fonksiyon" yerine "Callable" kullanılır. Dekoratörler genellikle yöntemler ve işlevler kapsamında tartışılırken, bunlarla sınırlı değildir. Aslında, çağrılabilir olan herhangi bir şey (_call__ yöntemini uygulayan herhangi bir nesne çağrılabilir olarak kullanılabilir), bir dekoratör olarak kullanılabilir ve çoğu zaman onlar tarafından döndürülen nesneler basit işlevler değil, kendi __call_ yöntemini uygulayan daha karmaşık sınıfların örnekleridir.

Dekoratör sözdizimi basitçe sadece sözdizimsel bir şekerdir . Aşağıdaki dekoratör kullanımını düşünün:

@some_decorator
def decorated_function():
    pass

Bu her zaman açık bir dekoratör çağrısı ve işlev yeniden ataması ile değiştirilebilir:

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)

Bununla birlikte, ikincisi daha az okunabilir ve tek bir işlevde birden fazla dekoratör kullanılıp kullanılmadığını anlamak çok zordur. Dekoratörler aşağıda gösterildiği gibi birden fazla farklı şekilde kullanılabilir:

İşlev olarak

Özel dekoratörler yazmanın birçok yolu vardır, ancak en basit yol, orijinal işlev çağrısını saran bir alt işlevi döndüren bir işlev yazmaktır.

Genel desenler aşağıdaki gibidir:

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

Sınıf olarak

Dekoratörler neredeyse her zaman işlevler kullanılarak uygulanabilse de, kullanıcı tanımlı sınıfları kullanmanın daha iyi bir seçenek olduğu bazı durumlar vardır. Dekoratör karmaşık parametrelendirmeye ihtiyaç duyduğunda veya belirli bir duruma bağlı olduğunda bu genellikle doğrudur.

Sınıf olarak parametrikleştirilmemiş bir dekoratörün genel deseni aşağıdaki gibidir:

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

Dekoratörleri parametrelendirme

Gerçek kodda, genellikle parametreleştirilebilen dekoratörler kullanmaya ihtiyaç vardır. İşlev bir dekoratör olarak kullanıldığında, çözüm basittir - ikinci bir sarma seviyesi kullanılmalıdır. Aşağıda, dekore edilmiş bir işlevin yürütülmesini her çağrıldığında belirtilen sayıda tekrarlayan basit bir dekoratör örneği verilmiştir:

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

Bu şekilde tanımlanan dekoratör parametreleri kabul edebilir:

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo

Parametreli dekoratör bağımsız değişkenleri için varsayılan değerlere sahip olsa bile, adından sonraki parantezlerin gerekli olduğunu unutmayın. Önceki dekoratörü varsayılan bağımsız değişkenlerle kullanmanın doğru yolu aşağıdaki gibidir:

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar

Son olarak Özellikleri olan dekoratörleri görelim.

Özellikleri

Özellikler, bir özniteliğin bir yöntem kümesine nasıl bağlanacağını bilen yerleşik bir tanımlayıcı türü sağlar. Bir özellik dört isteğe bağlı bağımsız değişken alır: fget, fset, fdel ve doc. Sonuncusu, özniteliğe bir yöntemmiş gibi bağlantılı bir doktrin tanımlamak için sağlanabilir. Aşağıda, iki köşe noktası depolayan özniteliklere doğrudan erişim veya width ve height özelliklerini kullanarak denetlenebilen bir Rectangle sınıfı örneği verilmiştir:

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
    )

Özellik oluşturmak için en iyi sözdizimi özelliği dekoratör olarak kullanmaktır. Bu , sınıf içindeki yöntem imzalarının sayısını azaltacak ve kodu daha okunaklı ve bakımı yapılabilir hale getirecektir . Dekoratörler ile yukarıdaki sınıf olur:

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

Başkalarının sahip olduklarını farklı bir şekilde söylemek için: evet, bir dekoratör.

Python'da şöyle:

  1. Bir işlev oluşturma (@ çağrısının altında bulunur)
  2. Oluşturduğunuz işlev üzerinde çalışmak için başka bir işlevi çağırmak. Bu yeni bir işlev döndürür. Aradığınız işlev @ argümanıdır.
  3. Tanımlanan işlevin, döndürülen yeni işlevle değiştirilmesi.

Bu fonksiyonlar nesneler ve sadece gerekli talimatlar olduğu için mümkün olan her türlü yararlı şey için kullanılabilir.


2

@ sembolü, bir plydata / pandas veri çerçevesi sorgusu içindeki değişkenlere erişmek için de kullanılır pandas.DataFrame.query. Misal:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.