[python] 🚀 Bir fonksiyon dekoratörü zinciri nasıl yapılır?
HTML Bu yolu gerçek bir uygulamada yapmaya çalışmıyorum - sadece dekoratörlerin ve dekoratör zincirinin nasıl çalıştığını anlamaya çalışıyorum.
Check out belgelere dekoratörler nasıl çalıştığını görmek için. İşte istediğin şey:
Paolo Bergantino2009-04-112009-04-11 07:16:18Z
Uzun açıklamalarda değilseniz, Paolo Bergentino'nun cevabını görün .
Python'un işlevleri nesnelerdir
Dekoratörleri anlamak için, öncelikle işlevlerin Python'da nesneler olduğunu anlamalısınız. Bunun önemli sonuçları var. Neden basit bir örnekle görelim:
Bunu aklında tut. Kısa süre sonra geri döneceğiz.
Python fonksiyonlarının bir başka ilginç özelliği ise başka bir fonksiyon içinde tanımlanabilmeleridir!
Tamam, hala burada mısın? Şimdi eğlenceli kısım ...
Bu, bir işlevin return başka bir işleve sahip olabileceği anlamına gelir .
return Bir işlev yapabilirseniz , birini parametre olarak iletebilirsiniz:
Dekoratörleri anlamak için gereken her şeye sahipsin. Görüyorsunuz, dekoratörler “sarmalayıcı” dır, yani fonksiyonun kendisini değiştirmeden dekore ettikleri fonksiyondan önce ve sonra kod çalıştırmanıza izin verirler .
Şimdi, muhtemelen çağrı her zaman bunu istiyorum a_stand_alone_function , a_stand_alone_function_decorated bunun yerine denir. Bu kolay, sadece a_stand_alone_function işlevinin döndürdüğü fonksiyonun üzerine yaz my_shiny_new_decorator :
Dekoratörler, dekoratör tasarım modelinin yalnızca bir pitonik varyantıdır . Geliştirmeyi kolaylaştırmak için Python'da yerleşik birkaç klasik tasarım deseni vardır (yineleyiciler gibi).
Elbette, dekoratörler biriktirebilirsiniz:
Python dekoratör sözdizimini kullanarak:
Dekoratörleri ayarladığınız sırayla MATTERS:
Şimdi: soruyu cevaplamak için ...
Dekoratörleri bir sonraki seviyeye çıkarmak
Dekore edilmiş işleve argümanlar gönderme
Python ile ilgili ilginç bir şey, yöntem ve fonksiyonların gerçekten aynı olmasıdır. Tek fark, yöntemlerin ilk argümanlarının o anki nesneye bir referans olmasını beklemesidir self .
Bu, aynı şekilde yöntemler için bir dekoratör kurabileceğiniz anlamına gelir! Sadece self dikkate almayı unutmayın :
Genel amaçlı bir dekoratör yapıyorsanız - biri ne olursa olsun argümanlarına bakılmaksızın herhangi bir işleve veya yönteme uygulanır - o zaman şunu kullanın *args, **kwargs :
Dekoratöre argümanlar gönderme
Harika, şimdi argümanların dekoratöre geçmesi hakkında ne söylersin?
Çözüm için acele etmeden önce, küçük bir hatırlatma yazalım:
Tamamen aynı. " my_decorator " denir. Öyleyse @my_decorator , Python'a '' " my_decorator " değişkeni ile etiketlenmiş işlevi çağırmasını söylüyorsunuz .
Bu önemli! Verdiğiniz etiket doğrudan dekoratöre işaret edebilir - ya da gösteremez .
Daha da kısaltalım :
Hey, gördün mü? " @ " Sözdiziminde işlev çağrısı kullandık ! :-)
Yani, bağımsız değişkenlerle dekoratörlere geri dönelim. Dekoratörü anında oluşturmak için işlevleri kullanabilirsek, bu işleve argümanları iletebiliriz, değil mi?
İşte burada: argümanları olan bir dekoratör. Argümanlar değişken olarak ayarlanabilir:
Gördüğünüz gibi, bu numarayı kullanan herhangi bir işlev gibi, dekoratöre argümanlar iletebilirsiniz. İsterseniz bile kullanabilirsiniz *args, **kwargs . Ancak dekoratörlerin sadece bir kez çağrıldığını unutmayın . Python sadece betiği aldığında. Argümanları daha sonra dinamik olarak ayarlayamazsınız. "X'i" içe aktardığınızda , işlev zaten dekore edilmiştir , bu nedenle hiçbir şeyi değiştiremezsiniz.
Hadi pratik yapalım: bir dekoratör dekore etmek
Tamam, bir bonus olarak, herhangi bir dekoratörün genel olarak herhangi bir tartışmayı kabul etmesi için bir pasaj vereceğim. Sonuçta, argümanları kabul etmek için, dekoratörümüzü başka bir fonksiyon kullanarak yarattık.
Biliyorum, en son bu hissi duyduğunda, bir adamın şöyle dinlemesinden sonraydı: "özyinelemeyi anlamadan önce özyinelemeyi anlamalısınız". Ama şimdi, bunu ustalaştırma konusunda kendini iyi hissetmiyor musun?
Dekoratörler Python 2.4'te tanıtıldı, bu nedenle kodunuzun> = 2.4'te çalıştırılacağından emin olun.
Bir işlevi çözemezsiniz. (Orada olan kaldırılabilir dekoratörler oluşturmak için kesmek, ama kimse bunları kullanır.) Bir fonksiyonu süslüdür Yani bir kez, o dekore tüm kodu .
Dekoratörler, hata ayıklamalarını zorlaştırabilen işlevleri sarar. (Bu Python> = 2.5'ten daha iyi olur; aşağıya bakınız.)
functools Modül Python 2.5 kullanılmaya başlandı. functools.wraps() Dekore edilmiş işlevin adını, modülünü ve belgesini paketleyicisine kopyalayan işlevi içerir .
Serin ve güçlü görünmek, ancak pratik bir örnek harika olurdu. Eh, 1000 olasılık var. Klasik kullanımlar, bir fonksiyon davranışını harici bir kütüphaneden genişletir (değiştiremezsiniz) veya hata ayıklamak için (geçici olduğu için değiştirmek istemezsiniz).
Bunları, bir DRY yöntemiyle birçok işlevi genişletmek için kullanabilirsiniz, şöyle:
Elbette dekoratörlerdeki en iyi şey, onları hemen hemen herhangi bir şey üzerinde yeniden yazmadan hemen kullanabilmenizdir. KURU dedim ki:
Python kendisi birkaç dekoratörler sağlamaktadır: property , staticmethod vb
Eşzamansız inlining asenkron fonksiyon çağrıları yapmak için bükülmüş.
Alternatif olarak, dekore edilmiş işlevin geri dönüş değerini fabrika işleve geçirilen bir etikette saran bir dekoratör döndüren bir fabrika işlevi yazabilirsiniz. Örneğin:
Dekoratör sözdiziminin bir steno olduğu inşaatı unutmayın:
Görünüşe göre diğer insanlar size sorunu nasıl çözeceğinizi söylemişlerdi. Umarım bu, dekoratörlerin ne olduğunu anlamanıza yardımcı olur.
Dekoratörler sadece sözdizimsel şeker.
Ve tabii ki bir dekoratör işlevinden de lambdaları geri getirebilirsiniz:
Rune Kaagaard2010-10-252010-10-25 06:18:12Z
Bir italik dekoratör olabilir
Bir fonksiyonun içinde bir fonksiyon tanımlandığına dikkat edin. Temel olarak yaptığı, yeni tanımlanmış olanla bir fonksiyonun yerini almaktır. Mesela bu sınıfa sahibim
Şimdi, her iki fonksiyonun da işlemden önce ve sonra "---" yazdırmasını istiyorum. Her baskı ifadesinden önce ve sonra "---" bir baskı ekleyebilirim. Ama kendimi tekrarlamaktan hoşlanmadığım için dekoratör yapacağım
Şimdi sınıfımı değiştirebilirim.
Dekoratörler hakkında daha fazla bilgi için http://www.ibm.com/developerworks/linux/library/l-cpdecor.html adresini ziyaret edin.
Abhinav Gupta2009-04-112009-04-11 07:19:12Z
İstediğiniz şeyi doğrudan aşağıda gösterildiği gibi yapan iki ayrı dekoratör yapabilirsiniz. Birden fazla argüman içeren dekore edilmiş işlevi destekleyen işlev *args, **kwargs bildiriminin kullanımına dikkat edin wrapped() (bu, örnek say() işlev için gerçekten gerekli değildir ancak genelliğe dahil edilmiştir).
Benzer nedenlerden dolayı, functools.wraps dekoratör sarılmış fonksiyonun meta özelliklerini, dekore edilmiş olanınkiler olarak değiştirmek için kullanılır. Bu hata mesajlarını ve gömülü işlev belgelendirmesini ( func.__doc__ ) wrapped() 'nin yerine dekore edilmiş işlevinkileri yapar.
Gördüğünüz gibi bu iki dekoratörde birçok kod var. Bu benzerlik göz önüne alındığında, bunun yerine, aslında bir dekoratör fabrikası olan başka bir deyişle başka dekoratörler yapan bir dekoratör işlevi olan jenerik bir ürün üretmeniz daha iyi olur. Bu şekilde daha az kod tekrarı olur ve DRY ilkesinin izlenmesine izin verilir .
Kodu daha okunaklı hale getirmek için, fabrikada üretilen dekoratörlere daha açıklayıcı bir ad atayabilirsiniz:
hatta onları şu şekilde birleştirin:
Yukarıdaki örnekler tüm işi yaparken, oluşturulan kod bir kerede birden fazla dekoratör uygulandığında, harici fonksiyon çağrıları biçiminde adil bir miktar ek yük içermektedir. Tam kullanıma bağlı olarak (örneğin I / O'ya bağlı olabilir) bu önemli olmayabilir.
Dekore edilmiş fonksiyonun hızı önemliyse, ek yük, tüm etiketleri bir kerede eklemeyi uygulayan biraz farklı bir dekoratör fabrikası fonksiyonunu yazarak tek bir ekstra fonksiyon çağrısına tutulabilir, böylece yapılan ilave fonksiyon çağrılarından kaçınan kod üretebilir. Her etiket için ayrı dekoratörler kullanarak.
Bu, dekoratörün kendisinde daha fazla kod gerektirir, ancak bu yalnızca işlev tanımlarına uygulandığında çalışır, daha sonra kendileri çağrıldığında değil. Bu aynı zamanda daha lambda önce gösterildiği gibi işlevleri kullanarak daha okunabilir isimler oluştururken de geçerlidir . Numune:
Bunu en basit şekilde yapmak için, işlevi kapatan lambdaları (anonim işlevler) döndüren dekoratörler yapın ve bunu adlandırın:
Ancak orijinal işlevi neredeyse kaybediyor gibiyiz.
Bunu bulmak için, biri diğerine gömülmüş olan her lambda kapanmasına dikkat etmemiz gerekir:
Dolayısıyla, bu fonksiyonun dokümantasyonunu koyarsak veya birden fazla argüman alan fonksiyonları dekore edebilmek istiyorsak veya sadece bir hata ayıklama oturumunda hangi fonksiyona baktığımızı bilmek istiyorsak, sargı.
Standart kütüphanedeki modülden wraps gelen dekoratör bizde functools !
Python 3'te ayrıca varsayılan olarak elde edilir __qualname__ ve __annotations__ atanır.
Bu nedenle wraps , sarma işlevinin hemen hemen her şeyi yapmasını sağladığını görüyoruz, ancak fonksiyonun argüman olarak ne aldığını bize anlatıyor.
Sorunu çözmeyi deneyebilecek başka modüller var, ancak çözüm henüz standart kütüphanede değil.
Aaron Hall2015-12-032015-12-03 18:09:54Z
Bir dekoratör fonksiyon tanımını alır ve bu fonksiyonu yerine getiren ve sonucu dönüştüren yeni bir fonksiyon yaratır.
Dekoratörün anlaşılması için, dekoratörün fonksiyonu yerine getiren ve sonucu dönüştüren içte yeni bir fonksiyon yarattığını fark etmek önemlidir.
Davoud Taghawi-Nejad2012-07-262012-07-26 16:11:42Z
Ne zaman yapalım:
Ayrıca dekoratör sınıfta yazabilirsiniz.
Birincisi, dekoratörlerin davranışını çok net bir hale getirdiğini düşünüyorum, ancak aynı zamanda yeni dekoratörleri çok özlü bir şekilde tanımlamayı da kolaylaştırıyor. Yukarıda listelenen örnek için, daha sonra şu şekilde çözebilirsiniz:
Ayrıca daha karmaşık görevleri yapmak için de kullanabilirsiniz, örneğin, işlevi bir yineleyici içinde tüm argümanlara otomatik olarak yinelemeli olarak uygulayan bir dekoratör gibi:
Bu örneğin list , dekoratörün başlatılmasında türü içermediğine dikkat edin, bu nedenle son baskı ifadesinde yöntem listenin öğelerini değil listenin kendisine uygulanır.
Sayıcı örneğinden bahsetmişken - yukarıda belirtildiği gibi, sayaç, dekoratörü kullanan tüm işlevler arasında paylaşılacaktır:
Bu şekilde, dekoratörünüz farklı işlevler için yeniden kullanılabilir (veya aynı işlevi birçok kez dekore etmek için kullanılabilir:) func_counter1 = counter(func); func_counter2 = counter(func) ve sayaç değişkeni her biri için özel kalır.
İşlevleri farklı argümanlarla dekore etme:
rabin utam2013-04-052013-04-05 18:18:08Z
Paolo Bergantin’in cevabı sadece stdlib kullanmanın büyük avantajına sahiptir ve dekoratör argümanlarının ve dekore edilmiş fonksiyon argümanlarının olmadığı bu basit örnek için işe yarar .
Bununla birlikte, daha genel vakalarla mücadele etmek istiyorsanız, 3 ana sınırlaması vardır:
Birkaç cevapta daha önce belirtildiği gibi, isteğe bağlı dekoratör argümanları eklemek için kodu kolayca değiştiremezsiniz . Örneğin makestyle(style='bold') , bir dekoratör oluşturmak önemsiz değildir.
Bunun yanında, yaratılmış olan paketler @functools.wraps imzayı korumaz , bu nedenle hatalı argümanlar verilirse uygulamaya başlar ve normalden farklı bir tür hataya yol açabilir TypeError .
Nihayet, bu oluşturulan sarma oldukça zordur @functools.wraps etmek adına bağlı bir argümanı erişmek . Aslında argüman içinde *args , içinde **kwargs veya hiç görünmeyebilir (isteğe bağlıysa).
Yazdığım decopatch ilk sorunu çözmek için, ve yazdığı makefun.wraps diğer ikisini çözmek için. Unutmayın makefun , aynı numarayı ünlü decorator lib ile aynı şekilde kullanıyor .
Bu, gerçekten imza koruyan sarmalayıcıları iade eden, argümanlara sahip bir dekoratör yaratma şeklinizdir:
decopatch tercihlerinize bağlı olarak, çeşitli python kavramlarını gizleyen veya gösteren iki geliştirme stili daha sunar. En kompakt stil aşağıdaki gibidir:
Lütfen ayrıntılar için belgelere bakın.
Node.js kullanılarak bir geri çağrı çağrılıncaya kadar bir işlevi nasıl beklersiniz?
Backtrace () / backtrace_symbols () işlev isimlerini nasıl yazdırır?
Kullanıcı tanımlı fonksiyon tanımlarının (“dokümanlar”) julia REPL için nasıl kullanılabilir hale getirilir?
Çalışma zamanında android Bold metninin bir parçası nasıl yapılır?
Clojure fonksiyonunun değişken sayıda parametre alması nasıl sağlanır?
Değişken bir şablon fonksiyonunun heterojen argüman paketleri üzerinde jenerik hesaplamalar nasıl yapılır?
C# satır içi işlevler nasıl yapılır
Önyükleme 3: Nasıl açılır menü bağlantısının gezinti çubuğunda tıklatılabilir hale getirilir
Genel Tür Dökme işlevi nasıl yapılır [çoğaltılır]
Bir işlevin bir işleve işaretçi döndürmesi nasıl yapılır? (C ++)
Bir .NET 4.0 projesinde bir .NET 2.0 karma mod derlemesine başvurmak için hangi 'ek yapılandırma' gereklidir?
Treesset vs Hashset
NodeJS / Express: “app.use” nedir?
PhoneGap ve Cordova komutları arasında bir fark var mı?
Normal argümanlar vs. anahtar kelime argümanları