Python-Entwurfsmuster: Für eleganten und modischen Code

Veröffentlicht: 2022-03-11

Sagen wir es noch einmal: Python ist eine höhere Programmiersprache mit dynamischer Typisierung und dynamischer Bindung. Ich würde es als eine mächtige, dynamische Sprache auf hohem Niveau beschreiben. Viele Entwickler lieben Python wegen seiner klaren Syntax, gut strukturierten Module und Pakete sowie wegen seiner enormen Flexibilität und modernen Features.

In Python zwingt Sie nichts dazu, Klassen zu schreiben und Objekte daraus zu instanziieren. Wenn Sie in Ihrem Projekt keine komplexen Strukturen benötigen, können Sie einfach Funktionen schreiben. Noch besser, Sie können ein flaches Skript schreiben, um eine einfache und schnelle Aufgabe auszuführen, ohne den Code überhaupt zu strukturieren.

Gleichzeitig ist Python eine zu 100 Prozent objektorientierte Sprache. Wie ist das? Nun, einfach gesagt, alles in Python ist ein Objekt. Funktionen sind Objekte, erstklassige Objekte (was auch immer das bedeutet). Diese Tatsache, dass Funktionen Objekte sind, ist wichtig, also denken Sie bitte daran.

Sie können also einfache Skripte in Python schreiben oder einfach das Python-Terminal öffnen und Anweisungen direkt dort ausführen (das ist so nützlich!). Gleichzeitig können Sie jedoch komplexe Frameworks, Anwendungen, Bibliotheken usw. erstellen. Sie können so viel in Python tun. Natürlich gibt es eine Reihe von Einschränkungen, aber das ist nicht das Thema dieses Artikels.

Da Python jedoch so leistungsfähig und flexibel ist, benötigen wir beim Programmieren einige Regeln (oder Muster). Lassen Sie uns also sehen, was Muster sind und wie sie sich auf Python beziehen. Wir werden auch damit fortfahren, einige wesentliche Python-Entwurfsmuster zu implementieren.

Warum ist Python gut für Muster?

Jede Programmiersprache ist gut für Muster. Tatsächlich sollten Muster im Kontext einer beliebigen Programmiersprache betrachtet werden. Sowohl die Muster als auch die Sprachsyntax und die Natur erlegen unserer Programmierung Beschränkungen auf. Die Einschränkungen, die sich aus der Sprachsyntax und der Sprachnatur (dynamisch, funktional, objektorientiert und dergleichen) ergeben, können unterschiedlich sein, ebenso wie die Gründe für ihre Existenz. Die Einschränkungen, die von Mustern ausgehen, sind aus einem bestimmten Grund da, sie sind zielgerichtet. Das ist das grundlegende Ziel von Mustern; uns zu sagen, wie wir etwas tun sollen und wie wir es nicht tun sollen. Wir werden später über Muster und insbesondere über Python-Entwurfsmuster sprechen.

Python ist eine dynamische und flexible Sprache. Python-Entwurfsmuster sind eine großartige Möglichkeit, sein enormes Potenzial auszuschöpfen.

Python ist eine dynamische und flexible Sprache. Python-Entwurfsmuster sind eine großartige Möglichkeit, sein enormes Potenzial auszuschöpfen.
Twittern

Die Philosophie von Python basiert auf der Idee gut durchdachter Best Practices. Python ist eine dynamische Sprache (habe ich das schon gesagt?) und implementiert als solche bereits eine Reihe beliebter Entwurfsmuster mit ein paar Zeilen Code oder macht es einfach, sie zu implementieren. Einige Entwurfsmuster sind in Python integriert, daher verwenden wir sie auch ohne es zu wissen. Andere Muster werden aufgrund der Natur der Sprache nicht benötigt.

Beispielsweise ist Factory ein strukturelles Python-Entwurfsmuster, das darauf abzielt, neue Objekte zu erstellen und die Instanziierungslogik vor dem Benutzer zu verbergen. Die Erstellung von Objekten in Python ist jedoch dynamisch, sodass Zusätze wie Factory nicht erforderlich sind. Es steht Ihnen natürlich frei, dies umzusetzen, wenn Sie möchten. Es mag Fälle geben, in denen es wirklich nützlich wäre, aber sie sind eine Ausnahme, nicht die Norm.

Was ist so gut an Pythons Philosophie? Beginnen wir damit (untersuchen Sie es im Python-Terminal):

 > >> 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!

Dies sind vielleicht keine Muster im herkömmlichen Sinne, aber es sind Regeln, die den „pythonischen“ Programmieransatz auf die eleganteste und nützlichste Weise definieren.

Wir haben auch PEP-8-Code-Richtlinien, die helfen, unseren Code zu strukturieren. Für mich ein Muss, natürlich mit einigen entsprechenden Ausnahmen. Übrigens werden diese Ausnahmen von PEP-8 selbst gefördert:

Aber am wichtigsten: Seien Sie inkonsequent – ​​manchmal passt der Styleguide einfach nicht. Verwenden Sie im Zweifelsfall Ihr bestes Urteilsvermögen. Sehen Sie sich andere Beispiele an und entscheiden Sie, was am besten aussieht. Und zögern Sie nicht zu fragen!

Kombinieren Sie PEP-8 mit The Zen of Python (ebenfalls ein PEP - PEP-20), und Sie haben eine perfekte Grundlage, um lesbaren und wartbaren Code zu erstellen. Fügen Sie Entwurfsmuster hinzu, und Sie sind bereit, jede Art von Softwaresystem mit Konsistenz und Entwicklungsfähigkeit zu erstellen.

Python-Entwurfsmuster

Was ist ein Designmuster?

Alles beginnt mit der Gang of Four (GOF). Führen Sie eine schnelle Online-Suche durch, wenn Sie mit der GOF nicht vertraut sind.

Entwurfsmuster sind eine gängige Methode zur Lösung bekannter Probleme. Zwei Hauptprinzipien liegen den von der GOF definierten Designmustern zugrunde:

  • Programmieren Sie eine Schnittstelle, keine Implementierung.
  • Bevorzugen Sie die Objektzusammensetzung gegenüber der Vererbung.

Schauen wir uns diese beiden Prinzipien aus der Perspektive von Python-Programmierern genauer an.

Programmieren Sie eine Schnittstelle, keine Implementierung

Denken Sie an Duck Typing. In Python definieren wir nicht gerne Schnittstellen und Programmklassen entsprechend dieser Schnittstellen, oder? Aber hör mir zu! Das bedeutet nicht, dass wir nicht über Schnittstellen nachdenken, bei Duck Typing tun wir das sogar ständig.

Lassen Sie uns ein paar Worte über den berüchtigten Duck Typing-Ansatz sagen, um zu sehen, wie er in dieses Paradigma passt: Programmieren an eine Schnittstelle.

Wenn es aussieht wie eine Ente und quakt wie eine Ente, dann ist es eine Ente!

Wenn es aussieht wie eine Ente und quakt wie eine Ente, dann ist es eine Ente!
Twittern

Wir kümmern uns nicht um die Natur des Objekts, wir müssen uns nicht darum kümmern, was das Objekt ist; wir wollen nur wissen, ob es in der Lage ist, das zu tun, was wir brauchen (uns interessiert nur die Schnittstelle des Objekts).

Kann das Objekt quaken? Also, lass es quaken!

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

Haben wir eine Schnittstelle für unsere Ente definiert? Nein! Haben wir an der Schnittstelle statt an der Implementierung programmiert? Jawohl! Und das finde ich so schön.

Wie Alex Martelli in seiner bekannten Präsentation über Design Patterns in Python betont: „Den Enten das Tippen beizubringen, dauert eine Weile, erspart Ihnen danach aber viel Arbeit!“

Bevorzugen Sie die Objektzusammensetzung gegenüber der Vererbung

Nun, das nenne ich ein pythonisches Prinzip! Ich habe weniger Klassen/Unterklassen erstellt als eine Klasse (oder häufiger mehrere Klassen) in eine andere Klasse zu packen.

Anstatt dies zu tun:

 class User(DbObject): pass

Wir können so etwas tun:

 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)

Die Vorteile liegen auf der Hand. Wir können einschränken, welche Methoden der umschlossenen Klasse verfügbar gemacht werden sollen. Wir können die Persister-Instanz zur Laufzeit injizieren! Zum Beispiel ist es heute eine relationale Datenbank, aber morgen könnte es alles sein, mit der Schnittstelle, die wir brauchen (wieder diese lästigen Enten).

Die Komposition ist für Python elegant und natürlich.

Verhaltensmuster

Verhaltensmuster beinhalten die Kommunikation zwischen Objekten, wie Objekte interagieren und eine bestimmte Aufgabe erfüllen. Nach GOF-Prinzipien gibt es in Python insgesamt 11 Verhaltensmuster: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template, Visitor.

Ich finde diese Muster sehr nützlich, aber das bedeutet nicht, dass die anderen Mustergruppen es nicht sind.

Iterator

Iteratoren sind in Python eingebaut. Dies ist eine der stärksten Eigenschaften der Sprache. Vor Jahren habe ich irgendwo gelesen, dass Iteratoren Python großartig machen, und ich denke, das ist immer noch der Fall. Wenn Sie genug über Python-Iteratoren und -Generatoren erfahren, wissen Sie alles, was Sie über dieses spezielle Python-Muster brauchen.

Verantwortungskette

Dieses Muster gibt uns die Möglichkeit, eine Anfrage mit verschiedenen Methoden zu behandeln, von denen jede einen bestimmten Teil der Anfrage adressiert. Wissen Sie, eines der besten Prinzipien für guten Code ist das Single-Responsibility -Prinzip.

Jedes Stück Code muss genau eine Sache tun.

Dieses Prinzip ist tief in dieses Designmuster integriert.

Wenn wir beispielsweise einige Inhalte filtern möchten, können wir verschiedene Filter implementieren, von denen jeder eine präzise und klar definierte Art der Filterung durchführt. Diese Filter können verwendet werden, um anstößige Wörter, Anzeigen, ungeeignete Videoinhalte usw. herauszufiltern.

 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)

Befehl

Dies ist eines der ersten Python-Entwurfsmuster, das ich als Programmierer implementiert habe. Da fällt mir ein: Muster werden nicht erfunden, sie werden entdeckt . Sie existieren, wir müssen sie nur finden und nutzen. Ich habe diesen für ein erstaunliches Projekt entdeckt, das wir vor vielen Jahren implementiert haben: einen speziellen WYSIWYM-XML-Editor. Nachdem ich dieses Muster intensiv im Code verwendet habe, habe ich auf einigen Seiten mehr darüber gelesen.

Das Befehlsmuster ist praktisch in Situationen, in denen wir aus irgendeinem Grund damit beginnen müssen, was ausgeführt werden soll, und es dann bei Bedarf auszuführen. Der Vorteil besteht darin, dass die Kapselung von Aktionen auf diese Weise es Python-Entwicklern ermöglicht, zusätzliche Funktionalitäten in Bezug auf die ausgeführten Aktionen hinzuzufügen, wie z.

Mal sehen, wie ein einfaches und häufig verwendetes Beispiel aussieht:

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

Schöpferische Muster

Beginnen wir damit, darauf hinzuweisen, dass Kreationsmuster in Python nicht häufig verwendet werden. Warum? Wegen der dynamischen Natur der Sprache.

Jemand, der klüger ist als ich, sagte einmal, dass Factory in Python integriert ist. Das bedeutet, dass uns die Sprache selbst all die Flexibilität bietet, die wir brauchen, um Objekte auf ausreichend elegante Weise zu erstellen; Wir müssen selten etwas darüber implementieren, wie Singleton oder Factory.

In einem Python-Designmuster-Tutorial fand ich eine Beschreibung der Entwurfsmuster, in der es heißt: „Muster bieten eine Möglichkeit, Objekte zu erstellen, während die Erstellungslogik verborgen wird, anstatt Objekte direkt mit einem neuen Operator zu instanziieren.“

Das fasst das Problem ziemlich gut zusammen: Wir haben keinen neuen Operator in Python!

Lassen Sie uns trotzdem sehen, wie wir ein paar implementieren können, falls wir das Gefühl haben, dass wir durch die Verwendung solcher Muster einen Vorteil erzielen könnten.

Einzelling

Das Singleton-Muster wird verwendet, wenn wir sicherstellen möchten, dass während der Laufzeit nur eine Instanz einer bestimmten Klasse existiert. Brauchen wir dieses Muster wirklich in Python? Meiner Erfahrung nach ist es einfacher, einfach absichtlich eine Instanz zu erstellen und sie dann zu verwenden, anstatt das Singleton-Muster zu implementieren.

Aber falls Sie es implementieren möchten, hier sind einige gute Nachrichten: In Python können wir den Instanziierungsprozess (zusammen mit praktisch allem anderen) ändern. Erinnerst du dich an die __new__() Methode, die ich zuvor erwähnt habe? Auf geht's:

 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

In diesem Beispiel ist Logger ein Singleton.

Dies sind die Alternativen zur Verwendung eines Singleton in Python:

  • Verwenden Sie ein Modul.
  • Erstellen Sie eine Instanz irgendwo auf der obersten Ebene Ihrer Anwendung, vielleicht in der Konfigurationsdatei.
  • Übergeben Sie die Instanz an jedes Objekt, das sie benötigt. Das ist eine Abhängigkeitsinjektion und ein mächtiger und leicht zu beherrschender Mechanismus.

Abhängigkeitsspritze

Ich beabsichtige nicht, in eine Diskussion darüber einzusteigen, ob Dependency Injection ein Entwurfsmuster ist, aber ich möchte sagen, dass es ein sehr guter Mechanismus zur Implementierung loser Kopplungen ist und dazu beiträgt, unsere Anwendung wartbar und erweiterbar zu machen. Kombiniere es mit Duck Typing und die Macht wird mit dir sein. Immer.

Ich habe es im Abschnitt Erstellungsmuster dieses Beitrags aufgeführt, weil es um die Frage geht, wann (oder noch besser: wo) das Objekt erstellt wird. Es wird draußen erstellt. Besser gesagt, die Objekte werden überhaupt nicht dort erstellt, wo wir sie verwenden, sodass die Abhängigkeit nicht dort erstellt wird, wo sie verbraucht wird. Der Verbrauchercode empfängt das extern erstellte Objekt und verwendet es. Als weitere Referenz lesen Sie bitte die am meisten positiv bewertete Antwort auf diese Stackoverflow-Frage.

Es ist eine schöne Erklärung der Abhängigkeitsinjektion und gibt uns eine gute Vorstellung vom Potenzial dieser speziellen Technik. Im Grunde erklärt die Antwort das Problem an folgendem Beispiel: Nicht selbst Getränke aus dem Kühlschrank holen, sondern einen Bedarf angeben. Sag deinen Eltern, dass du zum Mittagessen etwas zu trinken brauchst.

Python bietet uns alles, was wir brauchen, um das einfach zu implementieren. Denken Sie an die mögliche Implementierung in anderen Sprachen wie Java und C#, und Sie werden schnell die Schönheit von Python erkennen.

Lassen Sie uns über ein einfaches Beispiel für Abhängigkeitsinjektion nachdenken:

 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)

Wir fügen die Authenticator- und Authorizer -Methoden in die Command-Klasse ein. Alles, was die Command-Klasse braucht, ist, sie erfolgreich auszuführen, ohne sich um die Implementierungsdetails zu kümmern. Auf diese Weise können wir die Command-Klasse mit beliebigen Authentifizierungs- und Autorisierungsmechanismen verwenden, für die wir uns zur Laufzeit entscheiden.

Wir haben gezeigt, wie man Abhängigkeiten über den Konstruktor einfügt, aber wir können sie einfach einfügen, indem wir die Objekteigenschaften direkt festlegen, wodurch noch mehr Potenzial freigesetzt wird:

 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)

Es gibt noch viel mehr über Abhängigkeitsinjektion zu lernen; Neugierige würden zum Beispiel nach IoC suchen.

Aber bevor Sie das tun, lesen Sie eine andere Stackoverflow-Antwort, die am meisten positive auf diese Frage.

Auch hier haben wir gerade gezeigt, dass die Implementierung dieses wunderbaren Entwurfsmusters in Python nur eine Frage der Verwendung der integrierten Funktionen der Sprache ist.

Vergessen wir nicht, was das alles bedeutet: Die Abhängigkeitsinjektionstechnik ermöglicht sehr flexible und einfache Komponententests. Stellen Sie sich eine Architektur vor, in der Sie die Datenspeicherung spontan ändern können. Das Verspotten einer Datenbank wird zu einer trivialen Aufgabe, nicht wahr? Weitere Informationen finden Sie in Toptals Introduction to Mocking in Python.

Vielleicht möchten Sie auch Prototypen- , Builder- und Fabrikdesignmuster recherchieren.

Strukturmuster

Fassade

Dies ist möglicherweise das bekannteste Python-Entwurfsmuster.

Stellen Sie sich vor, Sie haben ein System mit einer beträchtlichen Anzahl von Objekten. Jedes Objekt bietet eine große Auswahl an API-Methoden. Sie können mit diesem System viele Dinge tun, aber wie wäre es mit einer Vereinfachung der Benutzeroberfläche? Warum nicht ein Schnittstellenobjekt hinzufügen, das eine gut durchdachte Teilmenge aller API-Methoden verfügbar macht? Eine Fassade!

Facade ist ein elegantes Python-Designmuster. Es ist eine perfekte Möglichkeit, die Benutzeroberfläche zu rationalisieren.

Facade ist ein elegantes Python-Designmuster. Es ist eine perfekte Möglichkeit, die Benutzeroberfläche zu rationalisieren.
Twittern

Beispiel für ein Python-Fassadendesignmuster:

 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

Es gibt keine Überraschung, keine Tricks, die Car ist eine Fassade , und das ist alles.

Adapter

Wenn Fassaden zur Vereinfachung der Benutzeroberfläche verwendet werden, geht es bei Adaptern ausschließlich darum, die Benutzeroberfläche zu ändern. Als würde man eine Kuh benutzen, wenn das System eine Ente erwartet.

Angenommen, Sie haben eine Arbeitsmethode zum Protokollieren von Informationen an einem bestimmten Ziel. Ihre Methode erwartet, dass das Ziel eine write() Methode hat (wie sie zum Beispiel jedes Dateiobjekt hat).

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

Ich würde sagen, es ist eine gut geschriebene Methode mit Abhängigkeitsinjektion, die eine große Erweiterbarkeit ermöglicht. Angenommen, Sie möchten sich bei einem UDP-Socket statt bei einer Datei anmelden. Sie wissen, wie man diesen UDP-Socket öffnet, aber das einzige Problem ist, dass das socket -Objekt keine Methode write() hat. Sie benötigen einen Adapter !

 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)

Aber warum finde ich Adapter so wichtig? Nun, wenn es effektiv mit Dependency Injection kombiniert wird, gibt es uns enorme Flexibilität. Warum unseren gut getesteten Code ändern, um neue Schnittstellen zu unterstützen, wenn wir einfach einen Adapter implementieren können, der die neue Schnittstelle in die bekannte übersetzt?

Sie sollten sich aufgrund ihrer Ähnlichkeit mit adapter auch Bridge- und Proxy -Designmuster ansehen und beherrschen. Überlegen Sie, wie einfach sie in Python zu implementieren sind, und denken Sie über verschiedene Möglichkeiten nach, wie Sie sie in Ihrem Projekt verwenden könnten.

Dekorateur

Oh, was haben wir für ein Glück! Dekorateure sind wirklich nett, und wir haben sie bereits in die Sprache integriert. Was mir an Python am besten gefällt, ist, dass uns bei der Verwendung von Python beigebracht wird, Best Practices anzuwenden. Es ist nicht so, dass wir uns der Best Practices (und insbesondere der Entwurfsmuster) nicht bewusst sein müssten, aber bei Python habe ich das Gefühl, dass ich trotzdem den Best Practices folge. Persönlich finde ich, dass Python Best Practices intuitiv und selbstverständlich sind, und das wird von Anfängern und Elite-Entwicklern gleichermaßen geschätzt.

Beim Decorator -Muster geht es darum, zusätzliche Funktionalität einzuführen und insbesondere ohne die Verwendung von Vererbung.

Schauen wir uns also an, wie wir eine Methode dekorieren, ohne die integrierte Python-Funktionalität zu verwenden. Hier ist ein einfaches Beispiel.

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

Was hier nicht so gut ist, ist, dass die execute viel mehr tut, als nur etwas auszuführen. Wir halten uns nicht buchstabengetreu an das Prinzip der Einzelverantwortung.

Es wäre gut, einfach nur Folgendes zu schreiben:

 def execute(action): return action()

Wir können jede Autorisierungs- und Authentifizierungsfunktion an einer anderen Stelle in einem Decorator wie folgt implementieren:

 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)

Jetzt ist die Methode execute() :

  • Einfach zu lesen
  • Macht nur eine Sache (zumindest wenn man sich den Code ansieht)
  • Ist mit Authentifizierung verziert
  • Ist mit Genehmigung dekoriert

Wir schreiben dasselbe mit der integrierten Decorator-Syntax von Python:

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

Es ist wichtig zu beachten, dass Sie nicht auf Funktionen als Dekorateure beschränkt sind. Ein Dekorateur kann ganze Klassen einbeziehen. Die einzige Voraussetzung ist, dass sie Callables sein müssen. Aber damit haben wir kein Problem; wir müssen nur die Methode __call__(self) definieren.

Vielleicht möchten Sie sich auch das functools-Modul von Python genauer ansehen. Da gibt es viel zu entdecken!

Fazit

Ich habe gezeigt, wie natürlich und einfach es ist, die Entwurfsmuster von Python zu verwenden, aber ich habe auch gezeigt, wie einfach das Programmieren in Python sein sollte.

„Einfach ist besser als komplex“, erinnern Sie sich daran? Vielleicht ist Ihnen aufgefallen, dass keines der Entwurfsmuster vollständig und formal beschrieben ist. Es wurden keine komplexen vollständigen Implementierungen gezeigt. Sie müssen sie „fühlen“ und so umsetzen, wie es Ihrem Stil und Ihren Bedürfnissen am besten entspricht. Python ist eine großartige Sprache und bietet Ihnen alle Möglichkeiten, um flexiblen und wiederverwendbaren Code zu erstellen.

Es bietet Ihnen jedoch mehr als das. Es gibt Ihnen die „Freiheit“, wirklich schlechten Code zu schreiben. Tu es nicht! Wiederholen Sie sich nicht (DRY) und schreiben Sie niemals Codezeilen, die länger als 80 Zeichen sind. Und vergessen Sie nicht, gegebenenfalls Entwurfsmuster zu verwenden; Es ist eine der besten Möglichkeiten, kostenlos von anderen zu lernen und von ihrem Erfahrungsschatz zu profitieren.