Python Tasarım Modelleri: Şık ve Modaya Uygun Kod İçin

Yayınlanan: 2022-03-11

Tekrar söyleyelim: Python, dinamik yazma ve dinamik bağlama özelliklerine sahip üst düzey bir programlama dilidir. Bunu güçlü, üst düzey dinamik bir dil olarak tanımlardım. Pek çok geliştirici, açık sözdizimi, iyi yapılandırılmış modülleri ve paketleri ve muazzam esnekliği ve çeşitli modern özellikleri nedeniyle Python'a aşıktır.

Python'da hiçbir şey sizi sınıflar yazmaya ve onlardan nesneler oluşturmaya zorlamaz. Projenizde karmaşık yapılara ihtiyacınız yoksa sadece fonksiyon yazabilirsiniz. Daha da iyisi, kodu yapılandırmadan bazı basit ve hızlı görevleri yürütmek için düz bir komut dosyası yazabilirsiniz.

Aynı zamanda Python yüzde 100 nesne yönelimli bir dildir. O nasıl? Basitçe söylemek gerekirse, Python'daki her şey bir nesnedir. İşlevler nesnelerdir, birinci sınıf nesnelerdir (bu ne anlama geliyorsa). İşlevlerin nesne olmasıyla ilgili bu gerçek önemlidir, bu yüzden lütfen bunu unutmayın.

Böylece, Python'da basit betikler yazabilir veya sadece Python terminalini açıp ifadeleri hemen orada çalıştırabilirsiniz (bu çok faydalı!). Ancak aynı zamanda karmaşık çerçeveler, uygulamalar, kitaplıklar vb. oluşturabilirsiniz. Python'da çok şey yapabilirsiniz. Elbette bir takım sınırlamalar var, ancak bu makalenin konusu bu değil.

Ancak Python çok güçlü ve esnek olduğu için programlama yaparken bazı kurallara (veya kalıplara) ihtiyacımız var. Öyleyse, kalıpların ne olduğunu ve Python ile nasıl ilişkili olduklarını görelim. Ayrıca birkaç temel Python tasarım modelini uygulamaya devam edeceğiz.

Python Modeller İçin Neden İyidir?

Herhangi bir programlama dili, kalıplar için iyidir. Aslında, kalıplar herhangi bir programlama dili bağlamında düşünülmelidir. Hem kalıplar, hem dil sözdizimi hem de doğa, programlamamıza sınırlamalar getirir. Dil sözdiziminden ve dil yapısından (dinamik, işlevsel, nesne yönelimli vb.) kaynaklanan sınırlamalar, varlıklarının arkasındaki nedenler gibi farklılık gösterebilir. Kalıplardan gelen sınırlamaların bir nedeni vardır, amaçlıdırlar. Kalıpların temel amacı budur; bize bir şeyin nasıl yapılıp nasıl yapılmayacağını anlatmak için. Kalıplar ve özellikle Python tasarım kalıpları hakkında daha sonra konuşacağız.

Python dinamik ve esnek bir dildir. Python tasarım desenleri, geniş potansiyelini kullanmanın harika bir yoludur.

Python dinamik ve esnek bir dildir. Python tasarım desenleri, geniş potansiyelini kullanmanın harika bir yoludur.
Cıvıldamak

Python'un felsefesi, iyi düşünülmüş en iyi uygulamalar fikri üzerine inşa edilmiştir. Python dinamik bir dildir (bunu zaten söylemiş miydim?) ve bu nedenle, birkaç satır kodla bir dizi popüler tasarım modelini zaten uygular veya uygulanmasını kolaylaştırır. Bazı tasarım desenleri Python'da yerleşiktir, bu yüzden onları bilmeden bile kullanırız. Dilin doğası gereği diğer kalıplara ihtiyaç duyulmaz.

Örneğin, Fabrika , yeni nesneler yaratmayı amaçlayan ve örnekleme mantığını kullanıcıdan gizlemeyi amaçlayan yapısal bir Python tasarım modelidir. Ancak Python'da nesnelerin oluşturulması tasarım gereği dinamiktir, bu nedenle Fabrika gibi eklemeler gerekli değildir. Tabii ki, isterseniz bunu uygulamakta özgürsünüz. Gerçekten yararlı olacağı durumlar olabilir, ancak bunlar istisnadır, norm değildir.

Python'un felsefesi hakkında bu kadar iyi olan nedir? Bununla başlayalım (Python terminalinde keşfedin):

 > >> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!

Bunlar geleneksel anlamda kalıplar olmayabilir, ancak bunlar programlamaya “Pythonic” yaklaşımını en zarif ve kullanışlı biçimde tanımlayan kurallardır.

Ayrıca kodumuzu yapılandırmaya yardımcı olan PEP-8 kod yönergelerimiz de bulunmaktadır. Tabii ki bazı uygun istisnalar dışında benim için bir zorunluluktur. Bu arada, bu istisnalar PEP-8'in kendisi tarafından teşvik edilmektedir:

Ancak en önemlisi: ne zaman tutarsız olunacağını bilin – bazen stil kılavuzu geçerli olmayabilir. Şüphe duyduğunuzda, en iyi kararınızı kullanın. Diğer örneklere bakın ve neyin en iyi göründüğüne karar verin. Ve sormaktan çekinmeyin!

PEP-8'i The Zen of Python (ayrıca bir PEP - PEP-20) ile birleştirin ve okunabilir ve sürdürülebilir kod oluşturmak için mükemmel bir temele sahip olacaksınız. Tasarım Modelleri ekleyin ve her türlü yazılım sistemini tutarlılık ve geliştirilebilirlik ile oluşturmaya hazırsınız.

Python Tasarım Modelleri

Tasarım Modeli Nedir?

Her şey Dörtlü Çete (GOF) ile başlar. GOF'a aşina değilseniz, hızlı bir çevrimiçi arama yapın.

Tasarım desenleri, iyi bilinen sorunları çözmenin yaygın bir yoludur. GOF tarafından tanımlanan tasarım modellerinin temelinde iki ana ilke vardır:

  • Bir uygulamaya değil bir arayüze programlayın.
  • Kalıtım yerine nesne kompozisyonunu tercih edin.

Bu iki ilkeye Python programcılarının bakış açısından daha yakından bakalım.

Bir uygulamaya değil bir arayüze programlayın

Ördek Yazmayı düşünün. Python'da arayüzler ve bu arayüzlere göre program sınıfları tanımlamayı sevmiyoruz, değil mi? Ama beni dinle! Bu, arayüzler hakkında düşünmediğimiz anlamına gelmez, aslında Duck Typing ile bunu her zaman yaparız.

Bu paradigmaya nasıl uyduğunu görmek için kötü şöhretli Duck Typing yaklaşımı hakkında birkaç söz söyleyelim: Programdan arayüze.

Ördeğe benziyorsa ve ördek gibi vaklıyorsa, o bir ördek!

Ördeğe benziyorsa ve ördek gibi vaklıyorsa, o bir ördek!
Cıvıldamak

Nesnenin doğasıyla ilgilenmiyoruz, nesnenin ne olduğuyla ilgilenmek zorunda değiliz; sadece ihtiyacımız olanı yapıp yapamayacağını bilmek istiyoruz (sadece nesnenin arayüzü ile ilgileniyoruz).

Nesne vaklayabilir mi? Öyleyse, bırak vakla!

 try: bird.quack() except AttributeError: self.lol()

Ördeğimiz için bir arayüz tanımladık mı? Numara! Uygulama yerine arayüze programladık mı? Evet! Ve bunu çok güzel buluyorum.

Alex Martelli'nin Python'daki Tasarım Kalıpları hakkındaki iyi bilinen sunumunda belirttiği gibi, "Ördeklere yazmayı öğretmek biraz zaman alır, ancak daha sonra size çok fazla iş kazandırır!"

Kalıtım yerine nesne kompozisyonunu tercih edin

İşte ben buna Pythonic prensibi diyorum! Bir sınıfı (veya daha sık olarak birkaç sınıfı) başka bir sınıfa sarmaya kıyasla daha az sınıf/alt sınıf oluşturdum.

Bunu yapmak yerine:

 class User(DbObject): pass

Bunun gibi bir şey yapabiliriz:

 class User: _persist_methods = ['get', 'save', 'delete'] def __init__(self, persister): self._persister = persister def __getattr__(self, attribute): if attribute in self._persist_methods: return getattr(self._persister, attribute)

Avantajları açıktır. Sarılmış sınıfın hangi yöntemlerinin açığa çıkarılacağını kısıtlayabiliriz. Kalıcı örneğini çalışma zamanında enjekte edebiliriz! Örneğin, bugün ilişkisel bir veritabanı, ancak yarın ihtiyacımız olan arayüzle (yine o sinir bozucu ördekler) ne olursa olsun olabilir.

Kompozisyon Python için zarif ve doğaldır.

Davranış kalıpları

Davranış Kalıpları, nesneler arasındaki iletişimi, nesnelerin nasıl etkileşime girdiğini ve verilen bir görevi nasıl yerine getirdiğini içerir. GOF ilkelerine göre Python'da toplam 11 davranış kalıbı vardır: Sorumluluk Zinciri, Komuta, Yorumlayıcı, Yineleyici, Arabulucu, Memento, Gözlemci, Durum, Strateji, Şablon, Ziyaretçi.

Bu kalıpları çok faydalı buluyorum ama bu diğer kalıp gruplarının olmadığı anlamına gelmiyor.

yineleyici

Yineleyiciler Python'da yerleşiktir. Bu, dilin en güçlü özelliklerinden biridir. Yıllar önce bir yerde yineleyicilerin Python'u harika kıldığını okumuştum ve bence durum hala böyle. Python yineleyicileri ve oluşturucuları hakkında yeterince bilgi edinin ve bu belirli Python modeli hakkında ihtiyacınız olan her şeyi bileceksiniz.

sorumluluk zinciri

Bu model bize, her biri isteğin belirli bir bölümünü ele alan farklı yöntemler kullanarak bir isteği ele almanın bir yolunu sunar. Biliyorsunuz, iyi kod için en iyi ilkelerden biri Tek Sorumluluk ilkesidir.

Her kod parçası bir ve sadece bir şey yapmalıdır.

Bu ilke, bu tasarım modeline derinlemesine entegre edilmiştir.

Örneğin, bazı içerikleri filtrelemek istiyorsak, her biri kesin ve açıkça tanımlanmış bir filtreleme türü yapan farklı filtreler uygulayabiliriz. Bu filtreler, rahatsız edici kelimeleri, reklamları, uygun olmayan video içeriğini vb. filtrelemek için kullanılabilir.

 class ContentFilter(object): def __init__(self, filters=None): self._filters = list() if filters is not None: self._filters += filters def filter(self, content): for filter in self._filters: content = filter(content) return content filter = ContentFilter([ offensive_filter, ads_filter, porno_video_filter]) filtered_content = filter.filter(content)

Emretmek

Bu, programcı olarak uyguladığım ilk Python tasarım modellerinden biri. Bu bana şunu hatırlatıyor: Kalıplar icat edilmez, keşfedilir . Onlar var, sadece onları bulup kullanmamız gerekiyor. Bunu yıllar önce uyguladığımız harika bir proje için keşfettim: özel amaçlı bir WYSIWYM XML düzenleyicisi. Bu kalıbı kodda yoğun bir şekilde kullandıktan sonra, bazı sitelerde bununla ilgili daha fazlasını okudum.

Komut kalıbı, herhangi bir nedenle, yürütülecek olanı hazırlayarak başlamamız ve daha sonra gerektiğinde yürütmemiz gereken durumlarda kullanışlıdır. Avantajı, eylemleri bu şekilde kapsüllemenin Python geliştiricilerinin yürütülen eylemlerle ilgili geri alma/yineleme gibi ek işlevler eklemesine veya eylemlerin geçmişini tutmasına ve benzerlerine olanak sağlamasıdır.

Basit ve sık kullanılan bir örneğin neye benzediğini görelim:

 class RenameFileCommand(object): def __init__(self, from_name, to_name): self._from = from_name self._to = to_name def execute(self): os.rename(self._from, self._to) def undo(self): os.rename(self._to, self._from) class History(object): def __init__(self): self._commands = list() def execute(self, command): self._commands.append(command) command.execute() def undo(self): self._commands.pop().undo() history = History() history.execute(RenameFileCommand('docs/cv.doc', 'docs/cv-en.doc')) history.execute(RenameFileCommand('docs/cv1.doc', 'docs/cv-bg.doc')) history.undo() history.undo()

Yaratıcı Desenler

Yaratılış kalıplarının Python'da yaygın olarak kullanılmadığına işaret ederek başlayalım. Niye ya? Dilin dinamik doğası nedeniyle.

Bir zamanlar benden daha akıllı biri, Factory'nin Python'da yerleşik olduğunu söyledi. Bu, dilin bize nesneleri yeterince zarif bir tarzda yaratmamız için ihtiyaç duyduğumuz tüm esnekliği sağladığı anlamına gelir; Singleton veya Factory gibi nadiren üstte herhangi bir şey uygulamamız gerekiyor.

Bir Python Tasarım Kalıpları eğitiminde, bu tasarım "kalıplar, nesneleri doğrudan yeni bir operatör kullanarak somutlaştırmak yerine, oluşturma mantığını gizlerken nesneler oluşturmanın bir yolunu sağlar" diyen yaratıcı tasarım kalıplarının bir tanımını buldum.

Bu, sorunu hemen hemen özetliyor: Python'da yeni bir operatörümüz yok!

Yine de, bu tür kalıpları kullanarak bir avantaj elde edebileceğimizi hissedersek, birkaçını nasıl uygulayabileceğimizi görelim.

tekton

Singleton modeli, çalışma zamanı sırasında belirli bir sınıfın yalnızca bir örneğinin var olduğunu garanti etmek istediğimizde kullanılır. Python'da bu kalıba gerçekten ihtiyacımız var mı? Deneyimlerime dayanarak, kasıtlı olarak bir örnek oluşturmak ve ardından Singleton modelini uygulamak yerine kullanmak daha kolay.

Ancak bunu uygulamak isterseniz, işte size bazı iyi haberler: Python'da, somutlaştırma sürecini değiştirebiliriz (hemen hemen her şeyle birlikte). Daha önce bahsettiğim __new__() yöntemini hatırlıyor musunuz? İşte başlıyoruz:

 class Logger(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_logger'): cls._logger = super(Logger, cls ).__new__(cls, *args, **kwargs) return cls._logger

Bu örnekte, Logger bir Singleton'dur.

Python'da Singleton kullanmanın alternatifleri şunlardır:

  • Bir modül kullanın.
  • Uygulamanızın en üst düzeyinde bir yerde, belki de yapılandırma dosyasında bir örnek oluşturun.
  • Örneği, ihtiyacı olan her nesneye iletin. Bu bir bağımlılık enjeksiyonudur ve güçlü ve kolayca yönetilebilen bir mekanizmadır.

Bağımlılık Enjeksiyonu

Bağımlılık enjeksiyonunun bir tasarım modeli olup olmadığı konusunda bir tartışmaya girmek niyetinde değilim, ancak bunun gevşek bağlantıların uygulanması için çok iyi bir mekanizma olduğunu ve uygulamamızın sürdürülebilir ve genişletilebilir olmasına yardımcı olduğunu söyleyeceğim. Duck Typing ile birleştirin ve Güç sizinle olsun. Hep.

Nesnenin ne zaman (hatta daha iyisi: nerede) oluşturulduğu sorusuyla ilgilendiğinden, onu bu yazının yaratılış modeli bölümünde listeledim. Dışarıda yaratılmıştır. Nesnelerin onları kullandığımız yerde yaratılmadığını söylemek daha iyidir, bu nedenle bağımlılık tüketildiği yerde yaratılmaz. Tüketici kodu, harici olarak oluşturulan nesneyi alır ve kullanır. Daha fazla referans için lütfen bu Stackoverflow sorusuna en çok oy alan yanıtı okuyun.

Bu, bağımlılık enjeksiyonunun güzel bir açıklaması ve bize bu özel tekniğin potansiyeli hakkında iyi bir fikir veriyor. Temel olarak cevap, sorunu şu örnekle açıklıyor: Buzdolabından içecek bir şey almayın, bunun yerine bir ihtiyacı belirtin. Anne babana öğle yemeğinde bir şeyler içmen gerektiğini söyle.

Python bize bunu kolayca uygulamak için ihtiyacımız olan her şeyi sunuyor. Java ve C# gibi diğer dillerdeki olası uygulamasını düşünün ve Python'un güzelliğini çabucak anlayacaksınız.

Basit bir bağımlılık enjeksiyonu örneğini düşünelim:

 class Command: def __init__(self, authenticate=None, authorize=None): self.authenticate = authenticate or self._not_authenticated self.authorize = authorize or self._not_autorized def execute(self, user, action): self.authenticate(user) self.authorize(user, action) return action() if in_sudo_mode: command = Command(always_authenticated, always_authorized) else: command = Command(config.authenticate, config.authorize) command.execute(current_user, delete_user_action)

Doğrulayıcı ve yetkilendirici yöntemlerini Command sınıfına enjekte ediyoruz. Command sınıfının tek ihtiyacı, uygulama ayrıntılarıyla uğraşmadan bunları başarıyla yürütmektir. Bu şekilde, çalışma zamanında kullanmaya karar verdiğimiz herhangi bir kimlik doğrulama ve yetkilendirme mekanizmasıyla Command sınıfını kullanabiliriz.

Yapıcı aracılığıyla bağımlılıkların nasıl enjekte edileceğini gösterdik, ancak bunları doğrudan nesne özelliklerini ayarlayarak, daha da fazla potansiyelin kilidini açarak kolayca enjekte edebiliriz:

 command = Command() if in_sudo_mode: command.authenticate = always_authenticated command.authorize = always_authorized else: command.authenticate = config.authenticate command.authorize = config.authorize command.execute(current_user, delete_user_action)

Bağımlılık enjeksiyonu hakkında öğrenilecek daha çok şey var; örneğin meraklı insanlar IoC'yi arayacaktır.

Ancak bunu yapmadan önce, bu soruya en çok oy alan başka bir Stackoverflow yanıtını okuyun.

Yine, Python'da bu harika tasarım modelinin nasıl dilin yerleşik işlevlerini kullanmaktan ibaret olduğunu gösterdik.

Tüm bunların ne anlama geldiğini unutmayalım: Bağımlılık enjeksiyon tekniği çok esnek ve kolay birim testine izin verir. Anında depolanan verileri değiştirebileceğiniz bir mimari hayal edin. Bir veritabanıyla alay etmek önemsiz bir iş haline gelir, değil mi? Daha fazla bilgi için Toptal'ın Python'da Alaycılığa Giriş kitabına göz atabilirsiniz.

Ayrıca Prototip , Oluşturucu ve Fabrika tasarım modellerini araştırmak isteyebilirsiniz.

Yapısal Modeller

Cephe

Bu en ünlü Python tasarım modeli olabilir.

Önemli sayıda nesneye sahip bir sisteminiz olduğunu hayal edin. Her nesne, zengin bir API yöntemleri seti sunar. Bu sistemle pek çok şey yapabilirsiniz ama arayüzü sadeleştirmeye ne dersiniz? Neden tüm API yöntemlerinin iyi düşünülmüş bir alt kümesini ortaya çıkaran bir arayüz nesnesi eklemiyorsunuz? Bir Cephe!

Cephe, zarif bir Python tasarım desenidir. Arayüzü düzene sokmanın mükemmel bir yolu.

Cephe, zarif bir Python tasarım desenidir. Arayüzü düzene sokmanın mükemmel bir yolu.
Cıvıldamak

Python Cephe tasarım deseni örneği:

 class Car(object): def __init__(self): self._tyres = [Tyre('front_left'), Tyre('front_right'), Tyre('rear_left'), Tyre('rear_right'), ] self._tank = Tank(70) def tyres_pressure(self): return [tyre.pressure for tyre in self._tyres] def fuel_level(self): return self._tank.level

Sürpriz yok, hile yok, Car sınıfı bir Cephe ve hepsi bu.

adaptör

Arayüzün basitleştirilmesi için Cepheler kullanılıyorsa, Adaptörler tamamen arayüzü değiştirmekle ilgilidir. Sistem bir ördek beklerken bir inek kullanmak gibi.

Belirli bir hedefe bilgi kaydetmek için çalışan bir yönteminiz olduğunu varsayalım. Yönteminiz, hedefin bir write() yöntemine sahip olmasını bekler (örneğin, her dosya nesnesinin sahip olduğu gibi).

 def log(message, destination): destination.write('[{}] - {}'.format(datetime.now(), message))

Büyük genişletilebilirlik sağlayan, bağımlılık enjeksiyonlu iyi yazılmış bir yöntem olduğunu söyleyebilirim. Diyelim ki bir dosya yerine bir UDP soketinde oturum açmak istiyorsunuz, bu UDP soketini nasıl açacağınızı biliyorsunuz ama tek sorun socket nesnesinin write() yönteminin olmaması. Bir Adaptöre ihtiyacınız var!

 import socket class SocketWriter(object): def __init__(self, ip, port): self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._ip = ip self._port = port def write(self, message): self._socket.send(message, (self._ip, self._port)) def log(message, destination): destination.write('[{}] - {}'.format(datetime.now(), message)) upd_logger = SocketWriter('1.2.3.4', '9999') log('Something happened', udp_destination)

Ama adaptörü neden bu kadar önemli buluyorum? Bağımlılık enjeksiyonu ile etkili bir şekilde birleştirildiğinde, bize büyük bir esneklik sağlıyor. Yeni arabirimi iyi bilinen arabirime çevirecek bir bağdaştırıcı uygulayabilecekken neden yeni arabirimleri desteklemek için iyi test edilmiş kodumuzu değiştirelim?

Ayrıca, adaptöre benzerlikleri nedeniyle köprü ve proxy tasarım modellerini de kontrol etmeli ve bunlara hakim olmalısınız. Python'da uygulamanın ne kadar kolay olduğunu düşünün ve bunları projenizde kullanmanın farklı yollarını düşünün.

Dekoratör

Ah ne kadar şanslıyız! Dekoratörler gerçekten güzel ve onları zaten dile entegre ettik. Python'da en sevdiğim şey, onu kullanmanın bize en iyi uygulamaları kullanmayı öğretmesidir. Bu, en iyi uygulamalar (ve özellikle tasarım kalıpları) konusunda bilinçli olmak zorunda olmadığımızdan değil, ancak Python ile ne olursa olsun en iyi uygulamaları takip ediyormuş gibi hissediyorum. Şahsen, Python'un en iyi uygulamalarının sezgisel ve doğal olduğunu düşünüyorum ve bu hem acemi hem de seçkin geliştiriciler tarafından takdir edilen bir şey.

Dekoratör kalıbı, ek işlevsellik kazandırmak ve özellikle bunu kalıtım kullanmadan yapmakla ilgilidir.

Öyleyse, yerleşik Python işlevselliğini kullanmadan bir yöntemi nasıl dekore ettiğimizi kontrol edelim. İşte basit bir örnek.

 def execute(user, action): self.authenticate(user) self.authorize(user, action) return action()

Burada o kadar iyi olmayan şey, execute işlevinin bir şeyi yürütmekten çok daha fazlasını yapmasıdır. Tek sorumluluk ilkesini harfi harfine takip etmiyoruz.

Sadece aşağıdakileri yazmak iyi olur:

 def execute(action): return action()

Herhangi bir yetkilendirme ve kimlik doğrulama işlevini başka bir yerde, bir dekoratörde şöyle uygulayabiliriz:

 def execute(action, *args, **kwargs): return action() def autheticated_only(method): def decorated(*args, **kwargs): if check_authenticated(kwargs['user']): return method(*args, **kwargs) else: raise UnauthenticatedError return decorated def authorized_only(method): def decorated(*args, **kwargs): if check_authorized(kwargs['user'], kwargs['action']): return method(*args, **kwargs) else: raise UnauthorizeddError return decorated execute = authenticated_only(execute) execute = authorized_only(execute)

Şimdi execute() yöntemi:

  • Okuması basit
  • Sadece bir şey yapar (en azından koda bakarken)
  • Kimlik doğrulama ile dekore edilmiştir
  • Yetki ile dekore edilmiştir

Aynı şeyi Python'un entegre dekoratör sözdizimini kullanarak yazarız:

 def autheticated_only(method): def decorated(*args, **kwargs): if check_authenticated(kwargs['user']): return method(*args, **kwargs ) else: raise UnauthenticatedError return decorated def authorized_only(method): def decorated(*args, **kwargs): if check_authorized(kwargs['user'], kwargs['action']): return method(*args, **kwargs) else: raise UnauthorizedError return decorated @authorized_only @authenticated_only def execute(action, *args, **kwargs): return action()

Dekoratör olarak işlevlerle sınırlı olmadığınızı not etmek önemlidir. Bir dekoratör tüm sınıfları içerebilir. Tek gereksinim, çağrılabilir olmaları gerektiğidir. Ama bununla bir sorunumuz yok; sadece __call__(self) yöntemini tanımlamamız gerekiyor.

Ayrıca Python'un functools modülüne daha yakından bakmak isteyebilirsiniz. Orada keşfedilecek çok şey var!

Çözüm

Python'un tasarım modellerini kullanmanın ne kadar doğal ve kolay olduğunu gösterdim, ancak Python'da programlamanın nasıl kolay olması gerektiğini de gösterdim.

"Basit, karmaşıktan daha iyidir", bunu hatırladın mı? Belki de tasarım modellerinden hiçbirinin tam ve resmi olarak tanımlanmadığını fark etmişsinizdir. Karmaşık tam ölçekli uygulamalar gösterilmedi. Bunları, tarzınıza ve ihtiyaçlarınıza en uygun şekilde “hissetmeniz” ve uygulamanız gerekir. Python harika bir dildir ve size esnek ve yeniden kullanılabilir kod üretmek için ihtiyacınız olan tüm gücü verir.

Ancak, size bundan daha fazlasını verir. Size gerçekten kötü kod yazma "özgürlüğü" verir. yapma! Kendinizi Tekrar Etmeyin (DRY) ve asla 80 karakterden uzun kod satırları yazmayın. Ve uygun olan yerlerde tasarım desenlerini kullanmayı unutmayın; başkalarından öğrenmenin ve onların zengin deneyimlerinden ücretsiz yararlanmanın en iyi yollarından biridir.