Python • Decorators.
Süslü fonksiyonlarla ilgili bu eğiticide, neye, nasıl oluşturulacağına ve kullanılacağına bakacağız. Süslü fonksiyonlar, yüksek mertebeden fonksiyonları çağırmak için basit bir sözdizimi sağlar.
Matematik ve bilgisayar bilimlerinde, daha yüksek mertebeden bir fonksiyon (aynı zamanda fonksiyonel, fonksiyonel form) aşağıdakilerden en az birini yapan bir fonksiyondur:
Tanım olarak, bir Süslü fonksiyon, başka bir fonksiyon alan ve açık bir şekilde değiştirmeden aldığı fonksiyonun davranışını genişleten bir fonksiyondur.
Bilgi: Python ile ilgili olarak bahsettiğimiz 'süslü fonksiyonlar' fonksiyonları ve yöntemleri daha kolay değiştirmemizi sağlayan Python sözdizimine özel bir değişikliktir. Tasarım desenleri bağlamında, süslü fonksiyonlar, alt sınıfları doğrudan kullanmak zorunda kalmadan bir fonksiyon, yöntem veya sınıfın fonksiyonselliğini dinamik olarak değiştirir. Bu, değiştirmek istemediğiniz fonksiyonlarin fonksiyonselliğini genişletmeniz gerektiğinde idealdir.
Esas olarak, süslü fonksiyonlar çevreleyici olarak fonksiyon görürler, fonksiyonun kendisini değiştirmeye gerek kalmadan, orijinal fonksiyonselliği arttırarak ve böylece süslemesini yaparak, kodun bir hedef fonksiyon yürütme işleminden önce ve sonra davranışlarını değiştirirler.
Bu kafa karıştırıcı gibi geliyor, ancak özellikle de süslü fonksiyonların nasıl çalıştığına dair birkaç örnek gördükten sonra değil. Bu makaledeki tüm örnekleri burada bulabilirsiniz.
Süslü fonksiyonları anlayabilmeniz için öncelikle fonksiyonların nasıl çalıştığını anlamanız gerekir. Bizim amacımız için, bir fonksiyon verilen ifadelere dayalı bir değer döndürür. İşte çok basit bir örnek:
Genel olarak, Python’daki fonksiyonlar, bir girdiyi bir çıktıya dönüştürmek yerine, yan etkilere de sahip olabilir. print()
fonksiyonu bunun temel bir örneğidir: Konsola bir şey vermenin yan etkisine sahipken, hiçbiri’ni döndürmez. Ancak, süslü fonksiyonlar anlamak için, fonksiyonları ifadeleri bir değere dönüştüren bir şey olarak düşünmek yeterlidir.
Note: Fonksiyonel programlamada, yan etkiler olmadan sadece saf fonksiyonlarla çalışırsınız (neredeyse). Tamamen fonksiyonel bir dil olmasa da, Python birinci sınıf öbekler gibi fonksiyonlar de dahil olmak üzere birçok fonksiyonel programlama kavramını desteklemektedir.
Bilgisayar biliminde, Fonksiyonel programlama, hesaplamayı matematiksel fonksiyonların değerlendirilmesi olarak hesaplamayı işleyen ve değişen durum ve değişebilir verileri önleyen bir programlama paradigmasıdır. Bir bildirimsel programlama paradigmasıdır, yani programlama, ifadeler yerine deyim veya bildirimlerle yapılır.
Fonksiyonel programlamada beyanlar-statements
- yerine ifadeler-expressions
- ve bildirimler-declarations
- kullanılır; bir programlama dilinde bir ifade, programlama dilinin başka bir değer üretmesi için yorumladığı ve hesapladığı bir veya daha fazla sabit, değişken, operatör ve fonksiyonlarin birleşimidir. Bu süreç, matematiksel ifadeler için değerlendirmeye denir.
Python’da fonksiyonlar birinci sınıf öbeklerdir. Bu fonksiyonlar diğer öbekler (string, int, float, list, vb.) gibi, fonksiyonların içinden geçirilip bir ifade olarak kullanılabileceği anlamına gelir. Aşağıdaki üç fonksiyonu göz önünde bulundurun:
Burada, merhaba_de()
ve harika_ol()
, bir dizge olarak verilen bir -ad- ismini bekleyen normal fonksiyonlardır. Ancak merhaba_kedi()
fonksiyonu, kendi ifadesi olarak bir fonksiyon -selamlama_fonk
- bekler. Örneğin, merhaba_de()
veya harika_ol()
fonksiyonuna iletebiliriz:
merhaba_kedi(merhaba_de)‘nin iki fonksiyona, ancak farklı yollara başvurduğunu unutmayın: harika_ol()
ve merhaba_de
. merhaba_de
fonksiyonu parantez içermiyor. Bu, fonksiyona yalnızca bir atfın geçirildiği anlamına gelir. Fonksiyon yürütülmez. Öte yandan, merhaba_kedi()
fonksiyonu parantez ile yazıldığından, her zamanki gibi çağrılır.
Diğer fonksiyonların içinde fonksiyonlar tanımlamak mümkündür. Bu gibi fonksiyonlar iç fonksiyonlar içinde çağrılır. İki iç fonksiyonlu bir fonksiyon örneği buradadır:
ebeveyn() fonksiyonu çağırdığınızda ne olur? Bir dakikalığına düşün bunu. Çıkış aşağıdaki gibi olacaktır:
İç fonksiyonların tanımlandığı sıranın önemli olmadığını unutmayın. Diğer fonksiyonlarda olduğu gibi, yazdırma yalnızca iç fonksiyonlar yürütüldüğünde gerçekleşir.
Ayrıca, üst fonksiyon çağrılana kadar iç fonksiyonlar tanımlanmamıştır. Bunlar yerel olarak ebeveyn()
ile kapsama alınıp araştırılır: bunlar yalnızca yerel değişkenler olarak ebeveyn()
fonksiyonu içinde bulunur. ilk_veled()
öğesini çağırmayı deneyin. Bir hata almalısınız:
ebeveyn() çağırdığınızda, ilk_veled()
ve ikinci_veled()
iç fonksiyonları da çağrılır. Ancak yerel kapsamı nedeniyle, ebeveyn()
fonksiyonu dışında mevcut değildir.
Python, fonksiyonları dönüş değeri olarak kullanmanıza da izin verir. Aşağıdaki örnek, dış ebeveyn() fonksiyonundan iç fonksiyonların birini döndürür.
Parantez olmadan ilk_veled
döndürdüğünüzü unutmayın. Bunun, ilk_veled
fonksiyonuna bir atıf döndürdüğünüz anlamına geldiğini hatırlayın. Aksine, ilk_veled()
parantez içinde, fonksiyonun değerlendirilmesi sonucunu ifade eder. Bu, aşağıdaki örnekte görülebilir:
Bir miktar şifreli çıktı, basitçe, ebeveyn()
içindeki, ikinci
değişkeni ikinci_veled()
fonksiyonuna işaret ederken ilk
değişkeni yerel ilk_veled()
fonksiyonuna başvurur.
İşaret ettikleri fonksiyonlara doğrudan erişilemese bile, artık normal fonksiyonlarmış gibi şimdi ilk
ve ikinci
kullanabilirsiniz:
Son olarak, daha önceki örnekte, iç fonksiyonları ebeveyn
fonksiyon içinde gerçekleştirdiğinizi unutmayın,ilk_veled()
özdeşi. Bununla birlikte, bu son örnekte, -return
- den sonra iç fonksiyonlara parantez eklemediniz -ilk_veled
-. Bu şekilde, gelecekte çağırabileceğiniz her bir fonksiyona bir atıf aldın. Mantıklı olmak?
Dalış yapmadan önce, net olması gereken bazı önkoşullar vardır. Python’da, fonksiyonlar birinci sınıf vatandaşlar, onlar öbeklerdir ve bu da onlarla çok yararlı şeyler yapabileceğimiz anlamına gelir.
Değişkenlere fonksiyon atama
Başka bir deyişle, diğer fonksiyonları üreten fonksiyonlar.
Daha çok bir kapanma olarak bilinir. Süslü fonksiyonlar inşa ederken karşılaşacağımız çok güçlü bir desen. Unutulmaması gereken bir başka şey ise, Python sadece dış bildirim alanını okumaya izin verir ve atamaya izin vermez. Yukarıdaki örneği, iç fonksiyonun kapalı kapsamından bir ‘name’ ifadeyi okumak ve yeni fonksiyonu döndürmek için nasıl değiştirdiğimizi fark ettik.
Süslü fonksiyonlar, mevcut fonksiyonları basit bir şekilde çevrelerler. Yukarıda bahsi geçen fikirleri bir araya getirerek bir süslü fonksiyon yapabiliriz. Bu örnekte, başka bir fonksiyonun string çıktısını p etiketleriyle saran bir fonksiyonu düşünelim.
Bu bizim ilk süslü fonksiyonumuzdu. Başka bir fonksiyonu ifade olarak alan bir fonksiyon, yeni bir fonksiyon üretir, orijinal fonksiyonun çalışmasını çoğaltır ve oluşturulan fonksiyonu döndürerek her yerde kullanabiliriz. get_text’in kendisi p_decorate tarafından donatılması için, sadece get_text ‘i p_decorate sonucuna atamak zorundayız.
Dikkat edilmesi gereken diğer bir şey, süslü fonksiyonumuzun bir isim ifadesini almasıdır. Süslü fonksiyonda yapmamız gereken her şey, get_text’in çevreleyicisinin bu ifadeyi geçmesine izin vermektir.
functools kütüphanesinden kısmi fonksiyonu kullanarak python’da kısmi fonksiyonlar oluşturabilirsiniz.
Kısmi fonksiyonlar, daha az parametre ve daha sınırlı fonksiyon için ayarlanan sabit değerler ile bir fonksiyona x parametresiyle bir fonksiyon türettirilmesini sağlar.
İçe aktarma gerekli:
Bu kod 8’e dönecek.
Önemli bir not: varsayılan değerler, değişkenleri soldan değiştirmeye başlar. 2 x’in yerini alacak. dbl(4) çağrıldığında y eşittir. Bu örnekte bir fark yaratmaz, ancak aşağıdaki örnekte yer almaktadır.
Bir süslü fonksiyon kullandığınızda, bir fonksiyonu bir diğeriyle değiştirirsiniz. Başka bir deyişle, bir süslü fonksiyonunuz varsa
o zaman siz
dersiniz.
söylemekle tamamen aynı şeydir.
ve fonksiyonunuzun f, with_logging fonksiyonuyla değiştirilir. Ne yazık ki, bu demek oluyor ki,
with_logging yazdıracağız, çünkü bu yeni fonksiyonunuzun adı. Aslında, eğer f için docstring belgesi değerine bakarsanız, boş olacaktır çünkü with_logging’ın hiçbir docstring belgesi değeri yoktur ve yazdığınız dokümanlar artık orada olmayacaktır. Ayrıca, bu fonksiyon için pydoc sonucuna bakarsanız, bir x ifadesi alarak listelenmeyecek; bunun yerine *args ve **kwargs olarak listelenecektir çünkü bu, with_logging’in ele aldığı şeydir.
Bir süslü fonksiyon kullanmak her zaman bir fonksiyon hakkında bu bilgiyi kaybetmek anlamına gelirse, ciddi bir sorun olur. Bu yüzden functools.wraps var. Bu, bir süslü fonksiyonda kullanılan bir fonksiyonu alır ve fonksiyon adı, docstring belgesi, ifade listesi vb. üzerine kopyalama fonksiyonelliğini ekler. Ve wraps kendisi bir süslü fonksiyon olduğundan, aşağıdaki kod doğru şeyi yapar:
Bugün hakkında konuşmak istediğim az bilinen bir araç var. Buna wraps denir ve functools modülünün bir parçasıdır. Doküman dizelerini ve süslü işlevlerin adlarını docstring belgelerini düzeltmek için bir @süslü fonksiyon olarak wraps kullanabilirsiniz. Bu neden önemli? Bu ilk başta garip bir durum gibi görünüyor, ancak bir API veya kendinizden başka birisinin kullanacağı herhangi bir kod yazıyorsanız, bu önemli olabilir. Nedeni, Python’un bir başkasının kodunu bulmak için içbakış kullandığınızda, süslü bir fonksiyon yanlış bilgileri döndürecektir. example.py olarak adlandırdığım basit bir örneğe bakalım:
help(example):
Bu kodda, ‘’‘a_function’’’ olarak adlandırılan fonksiyonu bir another_function ile donatırız. Fonksiyonun ‘’‘__name__’’’ ve ‘’‘__doc__’’’ özelliklerini kullanarak baskı yaparak ‘'’a_function’’‘’ın adını ve ‘'’docstring’’’ belgesi belgesini kontrol edebilirsiniz. Bu örneği çalıştırırsanız, çıktı için aşağıdakini alırsınız:
Bu doğru değil! Bu programı IDLE veya yorumlayıcıda çalıştırırsanız, bunun gerçekten kafa karıştırıcı, gerçekten hızlı bir şekilde nasıl elde edilebileceği daha da açık hale gelir. Bu gerçekten nasıl kafa karıştırıcı, gerçekten hızlı olabilir daha da belirgin hale geliyor.
Temel olarak burada olan şey, süslü fonksiyonun adını ve ‘'’docstring’’’ belgesini kendi başına değiştirmesidir.
Bu küçük karışıklığı nasıl düzeltiriz? Python geliştiricileri bize ‘'’functools.wraps’’’ çözümü verdi! Hadi kontrol edelim:
Burada ‘'’functools’’’ modülünden ‘'’wraps’’’ ları içe aktarıyoruz ve bir ‘'’another_function’’’ içindeki iç içe ‘'’wrapper’’’ fonksiyonu için bir süslü fonksiyon olarak kullanıyoruz. Bu kez onu çalıştırırsanız, çıktı değişmiş olacaktır:
Şimdi bir kez daha doğru isim ve docstring belgesi sahibiz. Python yorumlayıcınıza giderseniz, yardım fonksiyonu artık doğru şekilde çalışacaktır. Burada çıktısını koymaktan vazgeçip, denemeniz için onu bırakacağım.
help(example):
wraps süslü fonksiyonu hemen hemen bir numaradır, ama ihtiyaç duyduğunuzda oldukça kullanışlıdır. Eğer fonksiyonlarınızın size doğru ismi veya docstring belgesi vermediğini fark ederseniz, artık nasıl kolayca düzeltileceğini biliyorsunuz. Mutlu bir kodlama yapın!
Artık, bu fonksiyonların Python’daki diğer herhangi bir öbek gibi olduğunu gördüğünüze göre, Python süslü fonksiyonu olan büyülü yaratığa geçmeye ve görmeye hazırsınız. Bir örnekle başlayalım:
say_whee() çağırdığınızda ne olacağını tahmin edebilir misiniz? Dene:
Burada neler olduğunu anlamak için önceki örneklere bakın. Tam anlamıyla şimdiye kadar öğrendiğiniz her şeyi gerçekten uyguluyoruz.
Sözde fonksiyon süsleme şu satırda gerçekleşir:
Aslında, say_whee
ismi şimdi wrapper()
iç fonksiyonuna işaret etmektedir.
Çağrı yaptığınızda wrapper
fonksiyon olarak döndürdüğünüzü hatırlayın.
my_decorator(say_whee):
Ancak, wrapper()
fonksiyonu, orijinal say_whee()
fonksiyonune func olarak bir atıfta bulunur ve iki print()
çağrı arasında bu fonksiyonu çağırır.
Basitçe söylemek gerekirse: süslü fonksiyonlar, onların davranışlarını değiştirerek bir fonksiyonu çevrelemektedir.
Devam etmeden önce, ikinci bir örneğe bakalım. wrapper()
normal bir Python fonksiyonu olduğu için bir süslü fonksiyonun bir fonksiyonu değiştirdiği yöntem dinamik olarak değişebilir. Komşularınızı rahatsız etmeyecek şekilde, aşağıdaki örnek yalnızca gün boyunca süslü kodu çalıştıracaktır:
Yatma zamanı sonrası say_whee()
çağırmayı denerseniz, hiçbir şey olmaz:
Yukarıda say_whee()
ile fonksiyonu süslediğiniz yol biraz biçimsiz ve hantal. Her şeyden önce, say_whee
isminin üç kere yazımını bitirirsiniz. Buna ek olarak, fonksiyon süsleme, fonksiyon tanımının altında gizlenmiştir.
Bunun yerine, Python, bazen 'pie'
sözdizimi olarak da adlandırılan @
sembolüyle daha basit bir şekilde süslü fonksiyonlar kullanmanıza izin verir. Aşağıdaki örnek, ilk Süslü fonksiyon örneğiyle tamamen aynı şeyi yapar:
Bu yüzden, @my_decorator
sadece, say_whee = my_decorator(say_whee)
demenin daha kolay bir yoldur. Bu bir fonksiyona bir süslü fonksiyonun nasıl uygulandığıdır.
Bir süslü fonksiyonun sadece normal bir Python fonksiyonu olduğunu hatırlayın. Kolay tekrar kullanılabilirlik için tüm genel araçlar mevcuttur. Süslü fonksiyonu diğer birçok fonksiyonda kullanılabilecek kendi modülüne taşıyalım.
Aşağıdaki içerikle decorators.py
adlı bir dosya oluşturun:
Note: İçsel fonksiyonunuzu istediğiniz gibi adlandırın ve wrapper() gibi genel bir ad genellikle tamamdır. Bu makalede çok sayıda süslü fonksiyon göreceksiniz. Bunları ayrı tutmak için, iç fonksiyonu, süslü fonksiyonla aynı adla, ancak bir wrapper_ önekiyle adlandıracağız.
Bu yeni süslü fonksiyonu, normal bir içe aktarma yaparak diğer dosyalarda kullanabilirsiniz:
yada doğruda çalıştırabilirsiniz:
Bu örneği çalıştırdığınızda, orijinal say_whee()
öğesinin iki kez yürütüldüğünü görmelisiniz:
Şimdi, ilk örneğe dönelim ve uygulayalım. Burada, daha tipik olanı yapacağız ve aslında kodu süslü fonksiyonlarda kullanacağız:
süslü fonksiyonlarin artık çağrı etrafında 'Entering'
ve 'Exited'
izleme ifadelerine sahip olduğunu görebilirsiniz.
Kurucu, fonksiyon nesnesi olan bağımsız değişkeni saklar. Çağrıda, fonksiyonun adını göstermek için fonksiyonun __name__
özniteliğini kullanırız, daha sonra fonksiyonun kendisini çağırırız.
Meta sınıflar karmaşık bir konudur ve çoğu zaman ileri düzey programcılar, onlar için çok çeşitli pratik kullanımları görmez.
Gerçekten metasınıflar genellikle çok sayıda otomasyonun sağlanması gereken gelişmiş kütüphaneleri veya çerçeveleri programlarken oyuna girer. Örneğin, Django Forms sistemi, tüm sihrini sağlamak için meta sınıflara dayanır.
Bununla birlikte, genel olarak bilmediğimiz tüm teknikleri 'büyü'
ya da 'hileler'
olarak adlandırdığımızı ve Python’un bir sonucu olarak birçok şeyin bu şekilde çağrıldığını, bunun diğer dillere kıyasla genellikle kendine özgü bir uygulama olduğunu belirtmek zorundayız.
Biraz Python sihirbazlığı yapalım ve dilin gücünü kullanalım!
Bu yazıda size süslü fonksiyonlar ve meta sınıfların ilginç bir ortak kullanımını göstermek istiyorum. Yöntemleri işaretlemek için süslü fonksiyonların nasıl kullanılacağını size göstereceğim, böylece belirli bir işlemi gerçekleştirirken sınıf tarafından otomatik olarak kullanılabilirler.
Daha ayrıntılı olarak, bir dizgeye ‘işleyebilmek’ için çağrılabilecek bir sınıf uygulayacağım ve basit bir şekilde süslü yöntemlerle farklı ‘filtreleri’ nasıl uygulayacağınızı göstereceğim. Benim elde etmek istediğim şunun gibi:
Bu yazı içerisinde kullanılan kaynak kodun tamamını içerir.
Modül, bir standart imza (self, str)
içeren ve stringfilter ile donatılan ekleme yordamlarını alıp özelleştirebileceğim bir StringProcessor
sınıfı tanımlar. Bu sınıf daha sonra örneklenebilir ve örnek, bir dizeyi doğrudan işlemek ve sonucu döndürmek için kullanılır. Dahili olarak sınıf, tüm süslü yöntemleri art arda otomatik olarak yürütür. Ayrıca, sınıfın, filtreleri tanımladığım sıraya uymasını istiyorum: ilk önce, ilk olarak çalıştırılır.
Metaclasses bu hedefe ulaşmak için nasıl yardımcı olabilir?
Basitçe söylemek gerekirse, meta sınıflar, sınıfları almak için örneklendirilen sınıflardır. Bu, bir dersi her kullandığımda, örneğin, onu örneklendirmek için ilk Python’un, yazdığımız meta sınıfı ve sınıf tanımını kullanarak bu sınıfı oluşturduğu anlamına gelir. Örneğin, sınıf üyelerini __dict__
özniteliğinde bulabileceğinizi biliyorsunuz: bu özellik, tür olan standart metaclass tarafından oluşturulur.
Verilen bir metaclass, sınıf tanımındaki fonksiyonların bir alt kümesini tanımlamak için bir kod eklememiz için iyi bir başlangıç noktasıdır. Başka bir deyişle, meta sınıfın (yani, sınıfın) çıktısının tam olarak standart durumda olduğu gibi oluşturulmasını istiyoruz, ama ek olarak:
stringfilter ile süslenmiş tüm yöntemlerin ayrı bir listesi.
Bazı ifadeleri kabul eden bir fonksiyonunuz olduğunu varsayalım. Hala donatabilir misin? Hadi deneyelim:
Maalesef, bu kodu çalıştırmak bir hataya yol açıyor:
Sorun şu ki, içsel fonksiyon wrapper_do_twice()
herhangi bir ifade almaz, ancak name = 'World'
ona aktarıldı. wrapper_do_twice()
öğesinin bir ifadeyi kabul etmesine izin vererek bunu düzeltebilirsiniz, ancak daha önce oluşturduğunuz say_whee()
fonksiyonu için çalışmaz.
Çözüm, iç çevreleyici fonksiyonunda *args
ve **kwargs
kullanmaktır. Ardından, rastgele sayı ve konumsal ifadeyi kabul eder. decorators.py
dosyasını aşağıdaki gibi yeniden yazınız:
wrapper_do_twice iç fonksiyonu artık herhangi bir sayıdaki ifadeyi kabul eder ve bunları süslü fonksiyona iletir. Şimdi hem say_whee()
ve greet()
öbekleri çalışır:
Süslü fonksiyonların dönüş değeri ne olur? Eh, karar vermek süslü fonksiyona kalmış. Basit bir fonksiyonu aşağıdaki gibi donattığınızı varsayalım:
Kullanmayı dene:
Maalesef, süslü fonksiyonunuz fonksiyondan dönüş değerini yedi.
do_twice_wrapper()
fonksiyonu açıkça bir değer döndürmediğinden, return_greeting('Adam')
çağrısı, None döndürerek sona ermiştir.
Bunu düzeltmek için, çevrelenen fonksiyonunun süslü fonksiyonun dönüş değerini döndürdüğünden emin olmanız gerekir. decorators.py
dosyanızı 4. satırını ekleyerek değiştirin:
fonksiyonun son yürütülmesinden döndürülen değer döndürülür:
Kullanmayı dene:
Özellikle etkileşimli kabukta, Python ile çalışırken büyük kolaylık, güçlü içebakış yeteneğidir. İçebakış, bir nesnenin çalışma zamanında kendi öz niteliklerini bilmesidir. Örneğin, bir fonksiyon kendi adını ve belgelemesini bilir:
İçebakış, kendinizin tanımladığı fonksiyonlar için de çalışır:
Ancak, fonksiyon süslendikten sonra, say_whee()
kendi kimliği konusunda çok kafa karıştırdı. Şimdi do_twice() süslü fonksiyonunun içinde wrapper_do_twice()
iç fonksiyonu olduğunu bildiriyor. Teknik olarak doğru olsa da, bu çok yararlı bir bilgi değildir.
Bunu düzeltmek için, süslü fonksiyonlar orijinal fonksiyonla ilgili bilgileri muhafaza eden @functools.wraps
süslü fonksiyonunu kullanmalıdır. Decorators.py betiğini tekrar güncelleyin:
functools: - Yüksek mertebeden fonksiyonlar ve istenebilen nesneler üzerindeki işlemler. Functools modülü üst düzey fonksiyonlar içindir: diğer fonksiyonlar üzerinde hareket eden veya dönen fonksiyonlar. Genel olarak, herhangi bir değiştirilebilir nesne bu modülün amaçları için bir fonksiyon olarak ele alınabilir.Bu, bir wrapper fonksiyonunu tanımlarken update_wrapper() öğesini bir fonksiyon Süslü fonksiyon olarak çağırmak için bir kolaylık fonksiyonudur. Bu kısmi (update_wrapper, çevrelenmiş = çevrelenmiş, atanan = atanan, güncellenmiş = güncellenmiş) eşdeğerdir.
süslü say_whee()
fonksiyonu hakkında bir şey değiştirmeniz gerekmez:
Çok daha iyi! Şimdi say_whee()
, dekorasyondan sonra hala kendisidir.
Teknik Detay: functools.wraps süslü fonksiyon, içebakışta kullanılan __name__ ve __doc__ gibi özel nitelikleri güncellemek için functools.update_wrapper() fonksiyonunu kullanır.
Süslü fonksiyonların birkaç yararlı örneğine bakalım. Şimdiye kadar öğrendiklerinizle aynı kalıbı takip edeceklerini fark edeceksiniz.
Bu formül, daha karmaşık süslü fonksiyonlar oluşturmak için iyi bir standart kalıptır.
Not: Daha sonraki örneklerde, bu Süslü fonksiyonların de decorators.py dosyanıza kaydedildiğini kabul edeceğiz. Bu derste tüm örnekleri indirebileceğinizi unutmayın.
Bir timer
süslü fonksiyon oluşturarak başlayalım. Bir fonksiyonu yürütmek ve süreyi konsola yazdırmak için gereken süreyi ölçer. İşte kod:
Bu süslü fonksiyon, fonksiyonun çalışmaya başlamasından hemen önceki süreyi saklayarak çalışır (# 1 olarak işaretlenmiş satırda) ve fonksiyon bittikten hemen sonra (# 2’de). Fonksiyonun aldığı zaman ikisinin arasındaki farktır (# 3’de). Zaman aralıklarını ölçmek için iyi bir iş yapan time.perf_counter()
fonksiyonunu kullanırız.
Kendi kendinine çalışır. Kod boyunca satır satır çalışır. Nasıl çalıştığını anladığınızdan emin olun. Yine de anlamadıysan endişelenme. Süslü fonksiyonlar gelişmiş yapılardır. Ertelemeyi deneyin veya program akışını çizi.
Python2’de, print bir ifade sunan bir anahtar kelime oldu: print "Hi"
Python3’te print, çağrılabilecek bir fonksiyondur: print ("Hi")
.
Her iki sürümde, %
, sol tarafta bir dize ve sağ tarafta bir değer veya bir değer sayısı veya bir eşleme nesnesi gerektiren bir operatördür.
Satır böyle görünür: print(“a=%d,b=%d” % (f(x,n),g(x,n)))
Python3 için olan öneri, %
-style biçimlendirme yerine {}
-style biçimlendirme kullanmaktır:
print('a={:d}, b={:d}'.format(f(x,n),g(x,n)))
Python 3.6, başka bir dizgi formatlama paradigmasını sunar: f-dizeleri.
print(f'a={f(x,n):d}, b={g(x,n):d}')
Not: Eğer fonksiyonlarınızın çalışma zamanı hakkında bir fikir edinmek istiyorsanız, @timer decorator harikadır. Daha hassas ölçüm kodları yapmak istiyorsanız, yerine standart kütüphanedeki timeit modülünü düşünmelisiniz. Çöp toplanmasını geçici olarak devre dışı bırakır ve hızlı fonksiyon çağrılarından gelen gürültüyü çıkarmak için birden çok denemeyi çalıştırır.
Aşağıdaki @debug
süslü fonksiyon argümanları yazdıracaktır, fonksiyon çağrıldığında her seferinde bir fonksiyon, dönüş değeriyle birlikte çağrılır:
{signature}
, tüm argümanların dize gösterimlerine katılarak oluşturulur. Aşağıdaki listedeki sayılar, koddaki numaralı yorumlara karşılık gelir:
Konumsal ifadelerin bir listesini oluşturun. Her ifadeyi temsil eden güzel bir dize almak için repr()
öğesini kullanın.
keyword
ifadelerinin bir listesini oluşturun. value
ifade etmek için kullanılan repr()
‘yi ifade eden !r
belirtecinin olduğu yerde key=value
olarak f-string her bir ifadeyi formatlar.
Konumsal ve keyword ifadelerinin listeleri, bir virgülle ayrılan herbir ifade ile signature dizisinin birine beraber katılır.
Fonksiyon çalıştırıldıktan sonra geri dönüş değeri yazdırılır.
Süslü fonksiyonun, bir konum ve bir anahtar kelime ifadesiyle basit bir fonksiyona uygulayarak pratikte nasıl çalıştığını görelim:
@debug
süslü fonksiyonunun make_greeting()
fonksiyonunun signature
ve dönüş değerini nasıl yazdığını not alın:
Bu örnek, @debug
süslü fonksiyonu az önce yazdıklarınızı tekrarladığından beri kullanışlı görünmeyebilir. Doğrudan kendinize çağrı yapmadığınız küçük uygun fonksiyonlara uygulandığında daha güçlüdür.
Aşağıdaki örnek, matematik sabiti e’ye bir yaklaşım hesaplar:
Bu örnek ayrıca, bir süslü fonksiyonun önceden tanımlanmış bir fonksiyona nasıl uygulanabileceğini gösterir. e
‘nin yaklaşımı, aşağıdaki dizi genişlemesine dayanmaktadır:
e’nin değeri de 1/0! + 1/1! + 1/2! + 1/3! + 1/4! + 1/5! + 1/6! + 1/7! + ... (etc)
eşittir
Approximate_e()
fonksiyonunu çağırırken, @debug süslü fonksiyonunu iş başında görebilirsiniz:
Bu örnekte, sadece 5 terim ekleyerek, e = 2.718281828
gerçek değerine doğru bir yaklaşım elde edersiniz.
Python’da bir özel sınıf tanımladığınızda ve örneklerinden birini konsolda yazdırmaya çalıştığınızda (veya bir yorumlayıcı oturumunda inceleyin), göreceli olarak tatmin edici olmayan bir sonuç elde edersiniz.
Varsayılan ‘dizgi’ dönüştürme davranışı temeldir ve ayrıntılardan yoksundur:
Varsayılan olarak, aldığınız tek şey sınıf adını ve nesne örneğinin kimliğini içeren bir dizedir (bu, nesnenin CPython’daki bellek adresidir.) Bu, hiçbir şeyden daha iyi değildir, ancak aynı zamanda çok kullanışlı değildir.
Sınıfın özniteliklerini doğrudan yazdırarak veya sınıflarınıza özel bir to_string()
yöntemi ekleyerek, bu konu üzerinde çalışmaya deneyebilirsiniz.
Genel fikir burada doğru olanıdır -ancak Python’un nesnelerin dizge olarak nasıl temsil edildiğini işlemek için kullandığı sözleşmeleri ve yerleşik mekanizmaları yok sayar.
Bu sonraki örnek çok kullanışlı görünmeyebilir. Neden Python kodunuzu yavaşlatmak istersiniz? Muhtemelen en yaygın kullanım durumu, - bir web sayfası gibi - bir kaynağın olup olmadığını sürekli kontrol eden bir fonksiyonun hızını limitlemek istemenizdir. @slow_down
süslü fonksiyon, süslü fonksiyonu çağırmadan önce bir saniye uyuyacaktır:
“With “Why would you want to slow down your Python code? “ you’re questioning the other person’s motivations, which implies that a) it was deliberate and b) they might do it again c) you’re simply asking about someone’s motivation or opinions.”
@slow_down süslü fonksiyonunun etkisini görmek için, örneği kendiniz uygulamanız gerekiyor:
Not: countdown() fonksiyonu özyinelemeli bir fonksiyondur.Başka bir deyişle, kendisini çağıran bir fonksiyondur. Python'daki özyinelemeli fonksiyonlar hakkında daha fazla bilgi edinmek için, Python'da Tekrarlı Düşünmek kılavuzumuza bakın.
@slow_down
süslü fonksiyon her zaman bir saniye uyur. Daha sonra, süslü fonksiyona bir ifadeyi geçirerek oranı nasıl kontrol edeceğinizi göreceksiniz.
Süslü fonksiyonların, süsledikleri fonksiyonu çevrelemesi gerekmez. Ayrıca bir fonksiyonun mevcut olduğunu kolayca kaydedebilirler ve çevrelenmemiş olarak döndürebilirler. Bu kullanılabilir, örneğin, hafif bir eklenti mimarisi oluşturmak için kullanılabilir:
@register
süslü fonksiyonu, global PLUGINS dict
içindeki süslü fonksiyona bir atfı basitçe saklar. Bir iç işlev yazmanız veya bu örnekte @functools.wraps
kullanmanız gerekmez unutmayın, çünkü değiştirilmemiş orijinal işlevi döndürüyorsunuz.
Bilgi: Python'da bir süslü fonksiyon, ifade olarak başka bir fonksiyonu kabul eden bir fonksiyondur. Süslü fonksiyon genellikle kabul ettiği fonksiyonu değiştirir veya geliştirir ve değiştirilmiş fonksiyonu döndürür. Süslü bir fonksiyonu çağırdığınızda, temel tanımla karşılaştırıldığında ek özelliklere sahip olabilen, biraz farklı olabilecek bir fonksiyon alacağınız anlamına gelir.
randomly_greet()
fonksiyonu, kayıtlı fonksiyonlardan birini kullanmak için rasgele seçer. PLUGINS
sözlüğünün zaten eklenti olarak kaydedilen her bir fonksiyon öbeğine atıflar içerdiğini unutmayın:
Bu basit eklenti mimarisinin temel faydası, eklentilerin bulunduğu bir listeye bakımınızın gerekmemesidir. Bu liste, eklentiler kendilerini kaydettirdiğinde oluşturulur. Bu yeni bir eklenti eklemeyi önemsiz kılar: sadece fonksiyonu tanımlayın ve @register
ile süsleyin.
Python’da globals()
ile tanışıyorsanız, eklenti mimarisinin nasıl çalıştığıyla ilgili benzerlikler görebilirsiniz. globals()
, eklentileriniz de dahil olmak üzere geçerli kapsamdaki tüm global değişkenlere erişim sağlar:
@register
süslü fonksiyonunu kullanarak, globals()
fonksiyonlarından bazılarını etkin bir şekilde seçerek kendi seçilmiş ilginç değişkenler listenizi oluşturabilirsiniz.
web çerçevesiyle çalışırken yaygın olarak kullanılır
Bir web çatısıyla çalışırken yaygın olarak kullanılan bazı daha süslü fonksiyonlar için yola devam etmeden önce son örnek. Sadece giriş yapan kullanıcılar tarafından görülen veya başka şekilde doğrulanmış bir /secret
web sayfası kurmak için Flask kullanıyoruz:
Web çatınıza kimlik doğrulamanın nasıl ekleneceği hakkında bir fikir verirken, genellikle bu tür süslü fonksiyonları kendiniz yazmamalısınız. Flask için daha fazla güvenlik ve işlevsellik ekleyen Flask-Login uzantısını kullanabilirsiniz.
Şimdiye kadar, basit süslü fonksiyonların nasıl oluşturulduğunu gördünüz. Süslü fonksiyonların ne olduğunu ve nasıl çalıştıklarını çok iyi biliyorsunuz. Öğrendiğiniz herşeyi uygulamak için bu makaleden bir ara vermekte özgür hissedin.
Bu eğiticinin ikinci bölümünde, aşağıdakileri nasıl kullanacağınız da dahil olmak üzere daha gelişmiş özellikleri inceleyeceğiz:
Süslü Fonksiyon Sınıfları
Sınıflarda süslü fonksiyonlar kullanmanın iki farklı yolu vardır. İlki, zaten fonksiyonlarla yaptığınız şeye çok yakın: Bir sınıfın yordamlarını süsleyebilirsiniz. Süslü fonksiyonların gün içinde desteklenen tanıtım motivasyonlarından biriydi.
Python’da bile yerleşik olan bazı yaygın olarak kullanılan süslü fonksiyonlar
@classmethod
, @staticmethodv
ve @property
‘dir. @classmethod
ve
@staticmethod
süslü fonksiyonları, bu sınıfın belirli bir özdeşine bağlanmayan bir sınıf aduzayı içindeki yordamları tanımlamak için kullanılır. @property
süslü fonksiyonu, sınıf öznitellikleri için getters and setters
özelleştirmek için kullanılır. Bu süslü fonksiyonları kullanılarak bir örnek için aşağıdaki kutucuğu genişletin.
Bir Circle sınıfının aşağıdaki tanımı @classmethod
, @staticmethod
ve @property
süslü fonksiyonlarını kullanır:
Bu sınıfta:
.cylinder_volume()
normal bir yordamdır..radius
, değişken bir niteliktir: farklı bir değere ayarlanabilir. Ancak, bir setter metodu tanımlayarak, anlamsız bir negatif sayıya ayarlanmadığından emin olmak için bazı hata testleri yapabiliriz. Özelliklere parantez olmadan öznitelikler olarak erişilebilir..area
değişmez bir niteliktir: .setter()
yordamları dışındaki nitelikler, değiştirilemez. Bir yordam olarak tanımlanmış olsa bile, parantez içermeyen bir öznitellik olarak yeniden ele alınabilir..unit_circle()
bir sınıf yordamıdır. Belirli bir Circle
özdeşine bağlı değildir. Sınıf yordamları, sınıfın belirli özdeşlerini oluşturabilen fabrika yordamları olarak sıklıkla kullanılır..pi()
statik bir yordamdır. Circle
sınıfına gerçekten bağlı değildir, onun aduzayının bir parçası olması dışında. Statik yordamlar, bir özdeşte veya sınıfta çağrılabilir.Circle sınıfı örneğin aşağıdaki gibi kullanılabilir:
Önceden @debug
ve @timer
süslü fonksiyonlarını kullanarak bazı yordamlarını süslediğimiz bir sınıfı tanımlayalım:
Bu sınıfı kullanarak, süslü fonksiyonların etkisini görebilirsiniz:
Süslü fonksiyonları sınıflarda kullanmanın diğer yolu tüm sınıfı süslemektir. Bu, örneğin, Python 3.7’deki yeni dataclasses
modülünde yapılır:
Sözdiziminin anlamı, fonksiyon süslemelerine benzer. Yukarıdaki örnekte, Fonksiyon süslemesini PlayingCard = dataclass (PlayingCard)
yazarak yapabilirdiniz.
Sınıf süslü fonksiyonlarının yaygın bir kullanımı, metasınıfların bazı kullanım durumlarına daha basit bir alternatif olmaktır. Her iki durumda da, bir sınıfın tanımını dinamik olarak değiştiriyorsunuz.
Bir sınıf süslüsünün yazılması, bir fonksiyon süslüsünün yazılmasına çok benzer. Tek fark, süslü fonksiyonun bir ifade olarak bir fonksiyon değil bir sınıf alacağıdır. Aslında, yukarıda gördüğünüz tüm süslü fonksiyonlar, sınıf süslüleri olarak çalışacak. Onları bir fonksiyon yerine bir sınıfta kullanırken, bunların etkisi istediğiniz gibi olmayabilir. Aşağıdaki örnekte, @timer
süslü fonksiyonu bir sınıfa uygulanır:
Bir sınıfın süslenmesi yordamlarını süslemez. Hatırlayın, @timer
sadece TimeWaster = timer (TimeWaster)
için kısaltılmıştır.
Burada, @timer
sadece sınıfı eşleneği için gereken süreyi ölçer:
Sonra, uygun bir sınıf dekoratörünü tanımlayan, bir sınıfın sadece bir özdeşini sağlayan, @singleton
olarak adlandırılan bir örnek göreceksiniz.
Birbirlerine üst üste istifleyerek birkaç süslü fonksiyonu bir fonksiyona uygulayabilirsiniz:
Dekoratörlerin listelendikleri sırayla yürütüldüklerini düşünün. Başka bir deyişle, @debug
, greet()
veya debug(do_twice(greet()))
‘i çağıran
@do_twice
çağırır:
@debug
ve @do_twice
sırasını değiştirirsek farkı gözlemleyin:
Bu durumda, @do_twice
ayrıca @debug
‘a da uygulanacaktır:
Bazen, ifadeleri süslü fonksiyonlara iletmek yararlıdır. Örneğin, @do_twice
bir @repeat(num_times)
süslü fonksiyonuna genişletilebilir. Süslenen fonksiyonun yürütme sayısı, daha sonra bir ifade olarak verilebilir.
Bu, böyle bir şey yapmanıza izin verirdi:
Bunu nasıl başarabileceğinizi düşünün.
Şimdiye kadar, @
‘den sonra yazılan ad, başka bir fonksiyonla çağrılabilen bir fonksiyon öbeğine atfedildi. Tutarlı olmak için, o zaman bir süslü fonksiyon gibi davranabilen bir fonksiyon öbeğini döndürmek için repeat(num_times=4
) gerekir. Neyse ki, zaten fonksiyonların nasıl döndüğünü biliyorsunuz! Genel olarak, aşağıdaki gibi bir şey istersiniz:
Tipik olarak, süslü fonksiyon bir iç çevreleyici fonksiyonu oluşturur ve döndürür, bu yüzden örnek yazmak size içsel bir fonksiyon içinde bir iç fonksiyon kazandırır.
Biraz dağınık görünüyor, ama şimdiye kadar ifadeleri süslü fonksiyona işleyen bir ek def içinde birçok kez gördüğünüz aynı süslü fonksiyon modelini sadece ekledik. En içteki fonksiyonla başlayalım:
Yine, decorator_repeat()
, farklı adlandırılması dışında daha önce yazmış olduğunuz süslü fonksiyonlar gibi görünür. Bunun nedeni, kullanıcının arayacağı, en dıştaki fonksiyon için temel adı —repeat()
— ayırmamızdır.
Daha önce gördüğünüz gibi, en dıştaki fonksiyon, süslü fonksiyona bir atıf döndürür:
repeat() fonksiyonunda gerçekleşen birkaç ince şey vardır:
decorator_repeat()
öğesinin bir iç fonksiyon olarak tanımlanması, repeat()
öğesinin bir fonksiyon öbeğine atıfta bulunması anlamına gelir - decorator_repeat
. Daha önce, fonksiyon öbeğine atıfta bulunmak için parantez olmadan repeat
kullandık. Eklenen parantezler, ifadeleri alan süslü fonksiyonlar tanımlanırken gereklidir.num_times
ifadesi, görünüşte bizzat repeat()
içinde kendi kendine kullanılmamaktadır. Ancak num_times
değeri daha sonra wrapper_repeat()
tarafından kullanılıncaya kadar saklanır, num_times
geçerek bir kapanış oluşturulur.Ayarlanan her şeyle, sonuçların beklendiği gibi olup olmadığını görelim:
Sadece hedeflediğimiz sonuç.
Biraz dikkatli olursanız, hem ifadeli hem de ifadesiz olarak kullanılabilecek süslü fonksiyonlar da tanımlayabilirsiniz. Büyük ihtimalle buna ihtiyacınız yok, ancak esnekliğe sahip olmak güzel.
Bir önceki bölümde gördüğünüz gibi, bir süslü fonksiyon ifadeleri kullandığınızda, ilave bir dış fonksiyon eklemeniz gerekir. Meydan okuma, süslü fonksiyonların argümanlarla veya argümanlar olmadan çağrılıp çağrılmadığını anlamak içindir.
Süslü fonksiyonların ifadelerle veya ifadeler olmadan çağrılıp çağrılmadığını, kodunuzun çözmesi için meydan okumadır.
Süslü fonksiyon ifadeler olmadan çağrıldığında, fonksiyon, süslü fonksiyona yalnızca doğrudan iletildiğinden beri, fonksiyon isteğe bağlı bir ifade olmalıdır. Süslü fonksiyon ifadelerinin keyword
ile belirtilmesi gerektiği anlamına gelir. Bunu, aşağıdaki tüm parametrelerin yalnızca keyword olduğu anlamına gelen özel * sözdizimi ile uygulayabilirsiniz:
Süslü fonksiyonların ifadeler ile çağrılıp çağrılmadığını belirterek burada, _func
ifadesi bir işaretçi olarak davranır:
Eğer name ifadesiz çağrıldıysa, süslenmiş fonksiyon _func
olarak aktarılacaktır. İfadeler ile çağrıldıysa, o zaman _func
None olacaktır ve bazı keyword
ifadeleri varsayılan değerlerinden değişime uğramış olabilir. İfade listesindeki *, arda kalan ifadelerin konumsal ifadeler olarak çağrılamayacağı anlamına gelir.
Bu durumda, süslü fonksiyonlar, ifadeler ile çağrıldı. Bir fonksiyonu okuyabilen ve döndüren bir süslü fonksiyonu döndür.
Bu durumda, süslü fonksiyon, ifadeler olmadan çağrıldı. Süslü fonksiyonu hemen fonksiyona uygulayın.
Fonksiyonu çağırırken bir fonksiyona (veya yordama) geçirilen bir değer. İki tür ifade var:
complex()
için çağrılır:positional
ifade: bir keyword
ifade olmayan bir ifade. Konum ifadeleri, ifade listesinin başında görünebilir ve / veya * ile öncelenen yinelenebilir elemanlar olarak geçirilebilir. Örneğin, 3 ve 5, aşağıdaki her iki positional ifadesi çağrılır:İfadeler, bir fonksiyon gövdesindeki adlandırılmış yerel değişkenlere atanır. Sözdizimsel olarak, herhangi bir bildirim bir ifadeyi temsil etmek için kullanılabilir; değerlendirilen değer yerel değişkene atanır.
Eşit işaretin anlamı, tanımda mı yoksa çağrıda mı olduğuna bağlı olarak değişir. Tanımda, ifadeyi isteğe bağlı olarak işaretler ve varsayılan bir değere ayarlar. Çağrıda, istediğiniz ifadeleri ve değerleri belirtmenize izin verir.
Bir anahtar kelime ifadesi, varsayılan bir değere sahip bir konumsal ifadedir. Varsayılan değere sahip olmayan tüm ifadeleri belirtmelisiniz. Diğer bir deyişle, anahtar kelime ifadeleri yalnızca ‘isteğe bağlı’ dır, çünkü özellikle sağlanmadıkları takdirde varsayılan değerlerine ayarlanırlar.
Önceki bölümdeki @repeat
süslü fonksiyonunda bu şablonu kullanarak aşağıdakileri yazabilirsiniz:
Bunu orijinal @repeat
ile karşılaştırın. _func
parametresi ve sonunda if-else eklenmiş tek değişikliklerdir.
Bu örnekler, @repeat
‘un artık ifadelerle birlikte veya bağımsız olarak kullanılabileceğini gösterir:
num_times varsayılan değerinin 2 olduğunu hatırlayın:
Bazen, durumu takip edebilen bir süslü fonksiyon olması yararlı olur. Basit bir örnek olarak, bir fonksiyonun çağrılma sayısını sayan bir süslü fonksiyon oluşturacağız.
Not: Bu kılavuzun başında, verilen ifadelere dayalı bir değer döndüren saf fonksiyonlardan söz ettik. Durum bilgisi veren süslü fonksiyonlar tam tersidir, geri dönüş değeri mevcut duruma ve ayrıca verilen argümanlara bağlı olacaktır.
Bir sonraki bölümde, durumu korumak için sınıfları nasıl kullanacağınızı göreceksiniz. Ancak basit durumlarda, fonksiyon özniteliklerini kullanarak da uzaklaşabilirsiniz:
Durum - fonksiyona yapılan çağrı sayısı - çevreleyici fonksiyondaki .num_calls
fonksiyon özniteliğinde saklanır. İşte kullanmanın etkisi:
Durumu sürdürmenin tipik yolu, sınıfları kullanmaktır. Bu bölümde, bir süslü fonksiyon gibi bir sınıfı kullanarak bir önceki bölümden @count_calls
örneğinin nasıl yeniden yazılacağını göreceksiniz.
func = my_decorator(func)
demenin daha kolay bir yolu olan süslü fonksiyon sözdizimini @my_decorator hatırlayın. Bu nedenle, eğer my_decorator
bir sınıfsa, onun .__init__ ()
yordamında bir ifade olarak func alması gerekir. Dahası, sınıfın, süslü fonksiyonun içinde durabilmesi için, istenebilmesi gerekir.
Bir sınıfın istenebilir olması için, özel .__call__()
yordamını uygularsınız:
Bir sınıf özdeşini çağırmaya çalıştığınızda, __call__()
yordamı her seferinde yürütülür:
Bu nedenle, bir süslü fonksiyon sınıfının tipik bir uygulaması, .__init__()
ve .__call__()
uygulanması gerekir:
.__init__()
yordamı, fonksiyona bir atfı kaydetmeli ve diğer gerekli başlatma işlemlerini yapmalıdır. Süslü fonksiyonun yerine .__call__()
yordamı çağrılacak. Aslında daha önceki örneğimizde ‘'’wrapper()’’’ fonksiyonuyla aynı şeyi yapar.
@functools.wraps
yerine functools
. update_wrapper()
fonksiyonunu kullanmanız gerektiğini unutmayın.
@CountCalls
süslü fonksiyonu önceki bölümdeki ile aynı çalışır:
Her türlü süslü fonksiyonu nasıl oluşturacağımızı anlayarak, şu ana kadar çok yol kat ettik. Yeni bir bilgi birikimimizi, gerçek dünyada gerçekten faydalı olabilecek birkaç örnek oluşturarak bir araya getirelim.
Daha önce belirtildiği gibi, daha önce de belirtildiği gibi, Önceki @slow_down
uygulamamız her zaman bir saniye uyur. Artık, süslü fonksiyonlar için parametrelerin nasıl ekleneceğini biliyorsunuz. Bu nedenle, ne kadar süre uyuduğunu kontrol eden isteğe bağlı bir rate
ifadesi kullanarak @slow_down
‘u yeniden yazalım:
@slow_down
‘u ifadeli ve ifadesiz olarak kullanılabilir hale getirmek için, bölümünde tanıtılan katmanı kullanıyoruz. Aynı özyineli countdown()
fonksiyonu, daha önce olduğu gibi her sayım arasında iki saniye uyur:
Daha önce olduğu gibi, süslü fonksiyonun etkisini görmek için örneği kendiniz uygulamanız gerekir:
Singleton, tek bir özdeşe sahip bir sınıftır. Python’da, None
, True
ve False
da dahil olmak üzere sıkça kullandığınız birkaç singleton
vardır. Gerçekte None
, is
anahtar sözcüğünü kullanarak None
‘yu karşılaştırmanıza olanak veren bir singleton
‘dur
is
, yalnızca tamamen aynı özdeş öbekler için True
‘yu döndürür.
Aşağıdaki @singleton
süslü fonksiyonu, sınıfın ilk özdeşini bir öznitelik olarak saklayarak bir sınıfı bir singletona
dönüştürür.
Daha sonra saklanan özdeşi basitçe döndüren bir özdeş oluşturmaya çalışır:
Gördüğünüz gibi, bu sınıf donatıcı, fonksiyon donatıcılarımızla aynı şablonu takip eder. Tek fark, func
yerine cls
kullanmanın, bir sınıf donatıcı olması gerektiğine işaret etmek için parametre adı olarak kullanılmasıdır.
first_one’un gerçekten de bir another_one ile aynı özdeş olduğu açıkça görünüyor.
Not: Singleton sınıfları, diğer dillerdeki gibi Python'da sık sık kullanılmamaktadır. Bir Singleton etkisi genellikle bir modülde global değişken olarak daha iyi uygulanır.
Süslü fonksiyonlar, önbelleğe alma ve hafızalama için güzel bir mekanizma sağlayabilir. Örnek olarak, Fibonacci
dizisinin yinelemeli bir tanımına bakalım:
Uygulama basitken, çalışma zamanı performansı korkunç:
Onuncu Fibonacci sayısını hesaplamak için, gerçekten sadece önceki Fibonacci sayılarını hesaplamanız gerekir, ancak bu uygulama bir şekilde 177 hesaplamaya ihtiyaç duyar. Daha da kötüye gidiyor: Fibonacci(20)
için 22068 hesaplaması ve 30’uncu sayı için yaklaşık 2.7 milyon hesap. Bunun nedeni, kodun zaten bilinen Fibonacci sayılarını yeniden hesaplamasıdır.
Genel çözüm, bir for döngü ve bir arama tablosu kullanarak Fibonacci sayılarını uygulamaktır. Hesapların basit bir şekilde önbelleğe alınması da iş görecek:
Önbellek bir arama tablosu olarak çalışır, bu yüzden şimdi fibonacci()
sadece gerekli hesaplamaları bir kez yapar:
Son çağrıda fibonacci(8), sekizinci Fibonacci numarası zaten fibonacci(10)
için hesaplanmış olduğundan, yeni hesaplamalara gerek yoktu.
Standart kitaplıkta, en az kullanılan (LRU) cache, @functools.lru_cache
olarak kullanılabilir.
Bu süslü fonksiyon, yukarıda gördüğünüzden daha fazla özelliğe sahiptir. Kendi önbellek süslü fonksiyonunuzu yazmak yerine @functools.lru_cache
kullanmalısınız:
maxsize
parametresi, son aramaların kaç kere önbelleğe alındığını belirtir. Varsayılan değer 128’dir, ancak tüm fonksiyon çağrılarını önbelleğe almak için maxsize = None
değerini belirleyebilirsiniz. Ancak, birçok büyük nesneyi önbelleğe aldığınızda bunun bellek sorunlarına neden olabileceğini unutmayın.
Önbelleğin nasıl çalıştığını görmek için .cache_info()
yöntemini kullanabilir ve gerekirse bunları ayarlayabilirsiniz. Örneğimizde, önbellekten kaldırılan öğelerin etkisini görmek için yapay olarak küçük bir boyut belirledik:
Aşağıdaki örnek, daha önce, Eklenti Kaydetme örneğine benzemektedir, çünkü bu, süslü fonksiyonun davranışını gerçekten değiştirmez. Bunun yerine, yalnızca bir fonksiyon öznitelliği olarak unit ekler:
Aşağıdaki örnek yarıçapı ve santimetre cinsinden yüksekliği temelinde bir silindirin hacmini hesaplar:
Bu .unit
fonksiyon özniteliğine daha sonra gerektiğinde erişilebilir:
Fonksiyon ek açıklamalarını kullanarak benzer bir şeye ulaşabileceğinizi unutmayın:
Ancak, ek açıklamalar, tip ipuçları için kullanıldığı için statik tip kontrolü ile ek açıklama gibi birimleri birleştirmek zor olurdu
Birimler, birimler arasında dönüşebilen bir kütüphaneye bağlandığında daha da güçlü ve eğlenceli hale gelir. Böyle bir kütüphane de pint
. pint
yüklü olduğunda (pip install pint)
, örneğin hacmi; kübik inç veya galona dönüştürebilirsiniz:
Doğrudan bir pint
Niceliği döndürmek için süslü fonksiyonu de değiştirebilirsiniz. Böyle bir Nicelik, bir değeri birim ile çarparak yapılır. pint
de, birimler bir UnitRegistry
‘e bakılmalıdır. Kayıt defteri, ad alanının karışıklığını önlemek için bir fonksiyon özniteliği olarak saklanır:
@use_unit
süslü fonksiyonuyla, dönüştürme birimleri pratik olarak zahmetsizdir:
Son kullanım örneğine bakalım. Aşağıdaki Flask route rota fonksiyonuna hızlıca bakın:
Burada, student_id anahtarının request isteğin bir parçası olduğundan emin oluyoruz. Bu doğrulama çalışmasına rağmen, gerçekten fonksiyonun kendisine ait değildir. Ayrıca, belki de aynı doğrulamayı kullanan başka routes rotalarda vardır. Yani, hadi DRY’yi koruyalım ve bir süslü fonksiyon ile gereksiz bir mantığı soyutlayalım. Aşağıdaki @validate_json süslü fonksiyon, işi yapacak:
Yukarıdaki kodda, süslü fonksiyon, ifade olarak bir değişken uzunluk listesi alır; böylece, her biri JSON verilerini doğrulamak için kullanılan bir anahtarı temsil eden, gerektiği kadar dize ifadesini iletebiliriz:
Daha sonra, route rota işleyicisi, JSON verilerinin geçerli olduğunu güvenli bir şekilde varsaydığı için, gerçek işine (notları güncelleme) odaklanabilir:
Bu tam bir yolculuk oldu! Özellikle diğer fonksiyonlar içinde nasıl tanımlanabileceklerine ve diğer herhangi bir Python öbeği gibi nasıl geçirilebileceklerine; fonksiyonlara biraz daha yakından bakarak bu eğiticiye başladık. Sonra süslü fonksiyonları ve bunları nasıl yazacağınızı öğrendiniz:
Öğreticinin ikinci bölümünde daha gelişmiş süslü fonksiyonlar gördünüz ve nasıl yapılacağını öğrendiniz:
Bir süslü fonksiyon tanımlamak için, genellikle bir çevreleyici fonksiyon döndüren bir fonksiyon tanımladığını gördünüz. Çevreleyici fonksiyon, süslü fonksiyona ifadeleri iletmek için *args ve **kwargs kullanır. Süslü fonksiyonunuzunda ifadeler almasını istiyorsanız, çevreleyici fonksiyonu başka bir fonksiyonun içine yerleştirmeniz gerekir. Bu durumda, genellikle üç dönüş ifadesiyle sonuçlandırırsınız.