Yanıtlar:
@
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:
Karşılaşacağınız en yaygın Python dekoratörleri şunlardır:
@
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 @
.
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
/ class
a sonra tanımlarken dekoratör sadece temelde bir şekilde geçirilir argument
için function
/ method
hemen sonra @
işareti.
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ı.
app.route("/")
: Bu işlev, sizinle hello()
argüman olarak çağırdığınız bir işlevi döndürür
app.route("/", hello)
tanımladıktan hemen sonra çağırmak hello
, hatta hello
argümanlarda lambda olarak tanımlamak yerine app.route
? (İkinci örnek Node.js http.Server
ve Express rotalarında yaygındır .)
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.
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]]
@=
(yerinde) operatörünüz de vardır __imatmul__
.
__add__
ve __sub__
+ 'ya bağlı olması ve - sırasıyla ancak duymadım @
önce işaret biri. Orada gizlenen başkaları var mı?
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 a
ve b
matrislerdir.
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ö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
@wrapper
sö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 örneklerclassmethod()
vestaticmethod()
.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.)
İş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 .
@
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]])
@=
Ö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]])
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 decorator
gerekirse, 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_book
Ipython ü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_book
iç 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_book
sö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.
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
Python 3.5 ile başlayarak, '@' MATRIX MULTIPLICATION için özel bir infix sembolü olarak kullanılır (PEP 0465 - bkz. Https://www.python.org/dev/peps/pep-0465/ )
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
Başkalarının sahip olduklarını farklı bir şekilde söylemek için: evet, bir dekoratör.
Python'da şöyle:
Bu fonksiyonlar nesneler ve sadece gerekli talimatlar olduğu için mümkün olan her türlü yararlı şey için kullanılabilir.
Bir dekoratör kullandığınızı gösterir. İşte Bruce Eckel'in 2008 örneğidir .