Wzorce projektowe Pythona: dla eleganckiego i modnego kodu

Opublikowany: 2022-03-11

Powiedzmy to jeszcze raz: Python to język programowania wysokiego poziomu z dynamicznym typowaniem i dynamicznym wiązaniem. Opisałbym to jako potężny, dynamiczny język wysokiego poziomu. Wielu programistów jest zakochanych w Pythonie ze względu na jego przejrzystą składnię, dobrze ustrukturyzowane moduły i pakiety, a także ogromną elastyczność i zakres nowoczesnych funkcji.

W Pythonie nic nie zobowiązuje Cię do pisania klas i tworzenia z nich obiektów. Jeśli nie potrzebujesz skomplikowanych struktur w swoim projekcie, możesz po prostu napisać funkcje. Co więcej, możesz napisać płaski skrypt do wykonania prostego i szybkiego zadania bez jakiejkolwiek strukturyzowania kodu.

Jednocześnie Python jest językiem w 100% zorientowanym obiektowo. Jak to? Mówiąc prościej, wszystko w Pythonie jest obiektem. Funkcje to obiekty, obiekty pierwszej klasy (cokolwiek to znaczy). Ten fakt o funkcjach będących obiektami jest ważny, więc proszę o tym pamiętać.

Możesz więc pisać proste skrypty w Pythonie lub po prostu otworzyć terminal Pythona i wykonywać polecenia bezpośrednio tam (to bardzo przydatne!). Ale jednocześnie możesz tworzyć złożone frameworki, aplikacje, biblioteki i tak dalej. Tyle możesz zrobić w Pythonie. Oczywiście jest kilka ograniczeń, ale to nie jest temat tego artykułu.

Jednakże, ponieważ Python jest tak potężny i elastyczny, potrzebujemy pewnych reguł (lub wzorców) podczas programowania w nim. Zobaczmy więc, czym są wzorce i jak odnoszą się do Pythona. Przystąpimy również do implementacji kilku podstawowych wzorców projektowych w Pythonie.

Dlaczego Python jest dobry dla wzorców?

Każdy język programowania jest dobry dla wzorców. W rzeczywistości wzorce należy rozpatrywać w kontekście dowolnego języka programowania. Zarówno wzorce, składnia języka, jak i natura nakładają ograniczenia na nasze programowanie. Ograniczenia wynikające ze składni języka i natury języka (dynamiczny, funkcjonalny, obiektowy itp.) mogą być różne, podobnie jak przyczyny ich istnienia. Ograniczenia wynikające z wzorców są nie bez powodu, są celowe. To jest podstawowy cel wzorców; powiedzieć nam, jak coś zrobić, a jak tego nie robić. O wzorcach, a zwłaszcza wzorcach projektowych w Pythonie, omówimy później.

Python to dynamiczny i elastyczny język. Wzorce projektowe Pythona to świetny sposób na wykorzystanie jego ogromnego potencjału.

Python to dynamiczny i elastyczny język. Wzorce projektowe Pythona to świetny sposób na wykorzystanie jego ogromnego potencjału.
Ćwierkać

Filozofia Pythona jest zbudowana na idei dobrze przemyślanych najlepszych praktyk. Python jest językiem dynamicznym (czy już to powiedziałem?) i jako taki już implementuje lub ułatwia implementację wielu popularnych wzorców projektowych z kilkoma linijkami kodu. Niektóre wzorce projektowe są wbudowane w Pythona, więc używamy ich nawet bez wiedzy. Inne wzorce nie są potrzebne ze względu na charakter języka.

Na przykład Factory to strukturalny wzorzec projektowy Pythona, którego celem jest tworzenie nowych obiektów, ukrywając przed użytkownikiem logikę tworzenia instancji. Jednak tworzenie obiektów w Pythonie jest z założenia dynamiczne, więc dodatki takie jak Factory nie są konieczne. Oczywiście możesz go zaimplementować, jeśli chcesz. Mogą się zdarzyć przypadki, w których byłoby to naprawdę przydatne, ale to wyjątek, a nie norma.

Co jest takiego dobrego w filozofii Pythona? Zacznijmy od tego (poznaj to w terminalu Pythona):

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

To może nie są wzorce w tradycyjnym sensie, ale są to zasady, które definiują „pythonic” podejście do programowania w najbardziej elegancki i użyteczny sposób.

Mamy również wytyczne dotyczące kodu PEP-8, które pomagają ustrukturyzować nasz kod. To dla mnie konieczność, oczywiście z pewnymi odpowiednimi wyjątkami. Nawiasem mówiąc, do tych wyjątków zachęca sam PEP-8:

Ale co najważniejsze: wiedz, kiedy być niespójnym – czasami przewodnik po stylu po prostu nie ma zastosowania. W razie wątpliwości kieruj się własnym osądem. Spójrz na inne przykłady i zdecyduj, co wygląda najlepiej. I nie wahaj się zapytać!

Połącz PEP-8 z The Zen of Python (również PEP - PEP-20), a uzyskasz doskonałą podstawę do tworzenia czytelnego i łatwego w utrzymaniu kodu. Dodaj wzorce projektowe i jesteś gotowy do tworzenia każdego rodzaju systemu oprogramowania o spójności i możliwości rozwoju.

Wzorce projektowe Pythona

Co to jest wzór projektowy?

Wszystko zaczyna się od Gang of Four (GOF). Wykonaj szybkie wyszukiwanie w Internecie, jeśli nie znasz GOF.

Wzorce projektowe są powszechnym sposobem rozwiązywania dobrze znanych problemów. Dwie główne zasady leżą u podstaw wzorców projektowych zdefiniowanych przez GOF:

  • Program do interfejsu, a nie implementacji.
  • Preferuj kompozycję obiektów nad dziedziczenie.

Przyjrzyjmy się bliżej tym dwóm zasadom z perspektywy programistów Pythona.

Program do interfejsu, a nie implementacji

Pomyśl o pisaniu kaczki. W Pythonie nie lubimy definiować interfejsów i klas programów zgodnie z tymi interfejsami, prawda? Ale posłuchaj mnie! Nie oznacza to, że nie myślimy o interfejsach, w rzeczywistości z Duck Typing robimy to cały czas.

Powiedzmy kilka słów o niesławnym podejściu do pisania kaczego, aby zobaczyć, jak wpisuje się w ten paradygmat: program do interfejsu.

Jeśli wygląda jak kaczka i kwaka jak kaczka, to jest to kaczka!

Jeśli wygląda jak kaczka i kwaka jak kaczka, to jest to kaczka!
Ćwierkać

Nie przejmujemy się naturą przedmiotu, nie musimy dbać o to, czym jest przedmiot; chcemy tylko wiedzieć, czy jest w stanie zrobić to, czego potrzebujemy (interesuje nas tylko interfejs obiektu).

Czy obiekt może zakwakać? Więc niech zakwaka!

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

Czy zdefiniowaliśmy interfejs dla naszej kaczki? Nie! Czy programowaliśmy do interfejsu zamiast do implementacji? TAk! I uważam to za takie miłe.

Jak wskazuje Alex Martelli w swojej dobrze znanej prezentacji na temat wzorców projektowych w Pythonie: „Nauczenie kaczek pisania na klawiaturze zajmuje trochę czasu, ale oszczędza później wiele pracy!”

Preferuj kompozycję obiektów nad dziedziczeniem

To właśnie nazywam zasadą Pythona ! Utworzyłem mniej klas/podklas w porównaniu do pakowania jednej klasy (lub częściej kilku klas) w inną klasę.

Zamiast tego:

 class User(DbObject): pass

Możemy zrobić coś takiego:

 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)

Zalety są oczywiste. Możemy ograniczyć, jakie metody opakowanej klasy mają być eksponowane. Możemy wstrzyknąć instancję utrwalającą w czasie wykonywania! Na przykład dzisiaj jest to relacyjna baza danych, ale jutro może to być cokolwiek, z interfejsem, którego potrzebujemy (znowu te nieznośne kaczki).

Kompozycja jest dla Pythona elegancka i naturalna.

Wzorce zachowań

Wzorce behawioralne obejmują komunikację między obiektami, sposób interakcji obiektów i wypełniania danego zadania. Zgodnie z zasadami GOF, w Pythonie istnieje łącznie 11 wzorców zachowań: Łańcuch odpowiedzialności, Polecenie, Interpreter, Iterator, Mediator, Memento, Obserwator, Stan, Strategia, Szablon, Odwiedzający.

Uważam, że te wzorce są bardzo przydatne, ale to nie znaczy, że inne grupy wzorców nie są.

Iterator

Iteratory są wbudowane w Pythona. To jedna z najpotężniejszych cech języka. Wiele lat temu przeczytałem gdzieś, że iteratory czynią Pythona niesamowitym i myślę, że nadal tak jest. Dowiedz się wystarczająco dużo o iteratorach i generatorach Pythona, a dowiesz się wszystkiego, czego potrzebujesz na temat tego konkretnego wzorca Pythona.

Łańcuch odpowiedzialności

Ten wzorzec umożliwia nam potraktowanie żądania przy użyciu różnych metod, z których każda odnosi się do określonej części żądania. Wiesz, jedną z najlepszych zasad dobrego kodu jest zasada pojedynczej odpowiedzialności .

Każdy fragment kodu musi wykonać jedną i tylko jedną rzecz.

Ta zasada jest głęboko zintegrowana z tym wzorcem projektowym.

Na przykład, jeśli chcemy filtrować jakąś treść, możemy zaimplementować różne filtry, z których każdy wykonuje jeden precyzyjny i jasno określony rodzaj filtrowania. Filtry te mogą służyć do filtrowania obraźliwych słów, reklam, nieodpowiednich treści wideo i tak dalej.

 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)

Komenda

Jest to jeden z pierwszych wzorców projektowych w Pythonie, które zaimplementowałem jako programista. To mi przypomina: Wzory nie są wymyślane, są odkrywane . One istnieją, wystarczy je znaleźć i wykorzystać. Odkryłem go dla niesamowitego projektu, który zrealizowaliśmy wiele lat temu: specjalnego edytora XML WYSIWYM. Po intensywnym wykorzystaniu tego wzorca w kodzie, przeczytałem o nim więcej na niektórych stronach.

Wzorzec polecenia przydaje się w sytuacjach, gdy z jakiegoś powodu musimy zacząć od przygotowania tego, co zostanie wykonane, a następnie wykonać go w razie potrzeby. Zaletą jest to, że enkapsulacja akcji w taki sposób umożliwia programistom Pythona dodawanie dodatkowych funkcjonalności związanych z wykonywanymi akcjami, takich jak cofanie/ponawianie, prowadzenie historii akcji i tym podobne.

Zobaczmy, jak wygląda prosty i często używany przykład:

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

Wzorce twórcze

Zacznijmy od zwrócenia uwagi, że wzorce twórcze nie są powszechnie używane w Pythonie. Czemu? Ze względu na dynamiczną naturę języka.

Ktoś mądrzejszy niż kiedyś powiedział, że Factory jest wbudowane w Pythona. Oznacza to, że sam język zapewnia nam całą elastyczność niezbędną do tworzenia obiektów w wystarczająco elegancki sposób; rzadko musimy wdrażać coś na wierzchu, jak Singleton lub Factory.

W jednym z samouczków dotyczących wzorców projektowych Pythona znalazłem opis twórczych wzorców projektowych, które stwierdzały, że „wzorce projektowe zapewniają sposób tworzenia obiektów przy jednoczesnym ukryciu logiki tworzenia, zamiast tworzenia instancji obiektów bezpośrednio przy użyciu nowego operatora”.

To właściwie podsumowuje problem: nie mamy nowego operatora w Pythonie!

Niemniej jednak zobaczmy, jak możemy zaimplementować kilka, jeśli poczujemy, że możemy zyskać przewagę, używając takich wzorców.

Singel

Wzorzec Singleton jest używany, gdy chcemy zagwarantować, że w czasie wykonywania istnieje tylko jedna instancja danej klasy. Czy naprawdę potrzebujemy tego wzorca w Pythonie? Bazując na moim doświadczeniu, łatwiej jest po prostu celowo stworzyć jedną instancję, a następnie użyć jej zamiast implementować wzorzec Singleton.

Ale jeśli chcesz to zaimplementować, oto dobra wiadomość: w Pythonie możemy zmienić proces tworzenia instancji (wraz z praktycznie wszystkim innym). Pamiętasz __new__() , o której wspomniałem wcześniej? No to ruszamy:

 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

W tym przykładzie Logger to Singleton.

Oto alternatywy dla używania Singletona w Pythonie:

  • Użyj modułu.
  • Utwórz jedną instancję gdzieś na najwyższym poziomie aplikacji, być może w pliku konfiguracyjnym.
  • Przekaż instancję do każdego obiektu, który jej potrzebuje. To jest wstrzykiwanie zależności i jest to potężny i łatwy do opanowania mechanizm.

Wstrzykiwanie zależności

Nie zamierzam wdawać się w dyskusję na temat tego, czy wstrzykiwanie zależności jest wzorcem projektowym, ale powiem, że jest to bardzo dobry mechanizm implementacji luźnych sprzężeń i pomaga w utrzymaniu i rozszerzalności naszej aplikacji. Połącz to z Duck Typing, a Moc będzie z tobą. Zawsze.

Wymieniłem go w sekcji wzorców twórczych tego postu, ponieważ dotyczy on pytania, kiedy (a nawet lepiej: gdzie) obiekt jest tworzony. Jest tworzony na zewnątrz. Lepiej powiedzieć, że obiekty w ogóle nie są tworzone tam, gdzie ich używamy, więc zależność nie jest tworzona tam, gdzie jest zużywana. Kod konsumenta odbiera zewnętrznie stworzony obiekt i używa go. Aby uzyskać więcej informacji, przeczytaj najbardziej popularną odpowiedź na to pytanie Stackoverflow.

To miłe wyjaśnienie wstrzykiwania zależności i daje nam dobre wyobrażenie o potencjale tej konkretnej techniki. Zasadniczo odpowiedź wyjaśnia problem na następującym przykładzie: Nie wyjmuj rzeczy do picia samemu z lodówki, zamiast tego określ potrzebę. Powiedz rodzicom, że potrzebujesz czegoś do picia do obiadu.

Python oferuje nam wszystko, czego potrzebujemy, aby łatwo to zaimplementować. Pomyśl o jego możliwej implementacji w innych językach, takich jak Java i C#, a szybko zrozumiesz piękno Pythona.

Pomyślmy o prostym przykładzie wstrzykiwania zależności:

 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)

W klasie Command wstrzykujemy metody uwierzytelniające i autoryzujące . Wszystko, czego potrzebuje klasa Command, to pomyślne ich wykonanie bez zawracania sobie głowy szczegółami implementacji. W ten sposób możemy używać klasy Command z dowolnymi mechanizmami uwierzytelniania i autoryzacji, jakie zdecydujemy się użyć w czasie wykonywania.

Pokazaliśmy, jak wstrzykiwać zależności za pomocą konstruktora, ale możemy je łatwo wstrzyknąć, ustawiając bezpośrednio właściwości obiektu, odblokowując jeszcze większy potencjał:

 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)

Jest znacznie więcej do nauczenia się o wstrzykiwaniu zależności; ciekawi ludzie szukaliby na przykład IoC.

Ale zanim to zrobisz, przeczytaj inną odpowiedź Stackoverflow, najbardziej pochlebną na to pytanie.

Ponownie pokazaliśmy, że implementacja tego wspaniałego wzorca projektowego w Pythonie jest tylko kwestią użycia wbudowanych funkcji języka.

Nie zapominajmy, co to wszystko oznacza: technika wstrzykiwania zależności pozwala na bardzo elastyczne i łatwe testowanie jednostkowe. Wyobraź sobie architekturę, w której możesz zmieniać sposób przechowywania danych w locie. Mocowanie bazy danych staje się trywialnym zadaniem, prawda? Aby uzyskać więcej informacji, zapoznaj się z wprowadzeniem Toptal do Mocking w Pythonie.

Możesz także zbadać wzorce projektowe Prototype , Builder i Factory .

Wzory strukturalne

Fasada

To może być najbardziej znany wzorzec projektowy Pythona.

Wyobraź sobie, że masz system ze znaczną liczbą obiektów. Każdy obiekt oferuje bogaty zestaw metod API. Z tym systemem można zrobić wiele rzeczy, ale co z uproszczeniem interfejsu? Dlaczego nie dodać obiektu interfejsu udostępniającego dobrze przemyślany podzbiór wszystkich metod API? Fasada!

Fasada to elegancki wzorzec projektowy Pythona. To doskonały sposób na usprawnienie interfejsu.

Fasada to elegancki wzorzec projektowy Pythona. To doskonały sposób na usprawnienie interfejsu.
Ćwierkać

Przykładowy wzorzec projektowy elewacji w Pythonie:

 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

Nie ma niespodzianek, żadnych sztuczek, klasa Car to Fasada i to wszystko.

Adapter

Jeśli do uproszczenia interfejsu używane są fasady , w przypadku adapterów chodzi o zmianę interfejsu. Jak używanie krowy, gdy system spodziewa się kaczki.

Załóżmy, że masz działającą metodę rejestrowania informacji w określonym miejscu docelowym. Twoja metoda oczekuje, że miejsce docelowe będzie miało metodę write() (jak na przykład każdy obiekt pliku).

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

Powiedziałbym, że jest to dobrze napisana metoda z wstrzykiwaniem zależności, która pozwala na dużą rozszerzalność. Powiedzmy, że chcesz zalogować się do jakiegoś gniazda UDP zamiast do pliku, wiesz, jak otworzyć to gniazdo UDP, ale jedynym problemem jest to, że obiekt socket nie ma metody write() . Potrzebujesz adaptera !

 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)

Ale dlaczego adapter jest dla mnie tak ważny? Cóż, gdy jest to skutecznie połączone z wstrzykiwaniem zależności, daje nam ogromną elastyczność. Po co zmieniać nasz dobrze przetestowany kod, aby obsługiwał nowe interfejsy, skoro możemy po prostu zaimplementować adapter, który przetłumaczy nowy interfejs na dobrze znany?

Należy również sprawdzić i opanować wzorce projektowe mostów i serwerów proxy , ze względu na ich podobieństwo do adapter . Pomyśl, jak łatwo można je zaimplementować w Pythonie i zastanów się, w jaki sposób możesz ich użyć w swoim projekcie.

Dekorator

Och, jakie mamy szczęście! Dekoratorzy są naprawdę fajni i już je zintegrowaliśmy z językiem. W Pythonie najbardziej podoba mi się to, że jego używanie uczy nas stosowania najlepszych praktyk. Nie chodzi o to, że nie musimy być świadomi najlepszych praktyk (w szczególności wzorców projektowych), ale w Pythonie czuję, że podążam za najlepszymi praktykami, niezależnie od tego. Osobiście uważam, że najlepsze praktyki Pythona są intuicyjne i mają drugą naturę, co doceniają zarówno nowicjusze, jak i elitarni programiści.

Wzorzec dekoratora polega na wprowadzeniu dodatkowej funkcjonalności, a w szczególności wykonaniu tego bez dziedziczenia.

Zobaczmy więc, jak dekorujemy metodę bez użycia wbudowanej funkcjonalności Pythona. Oto prosty przykład.

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

Co nie jest tak dobre, to to, że funkcja execute robi znacznie więcej niż wykonywanie czegoś. Nie kierujemy się zasadą pojedynczej odpowiedzialności co do joty.

Dobrze byłoby po prostu napisać po prostu:

 def execute(action): return action()

Możemy zaimplementować dowolną funkcjonalność autoryzacji i uwierzytelniania w innym miejscu, w dekoratorze , np.:

 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)

Teraz metoda execute() to:

  • Prosty do przeczytania
  • Robi tylko jedną rzecz (przynajmniej patrząc na kod)
  • Jest ozdobiony uwierzytelnianiem
  • Posiada autoryzację

Piszemy to samo, używając zintegrowanej składni dekoratora Pythona:

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

Ważne jest, aby pamiętać, że nie jesteś ograniczony do funkcji dekoratorów. Dekorator może obejmować całe klasy. Jedynym wymaganiem jest to, że muszą być wywoływalne . Ale nie mamy z tym problemu; wystarczy zdefiniować metodę __call__(self) .

Możesz także przyjrzeć się bliżej modułowi functools Pythona. Jest tam wiele do odkrycia!

Wniosek

Pokazałem, jak naturalne i łatwe jest używanie wzorców projektowych Pythona, ale pokazałem też, jak łatwe powinno być programowanie w Pythonie.

„Proste jest lepsze niż złożone”, pamiętasz? Być może zauważyłeś, że żaden z wzorców projektowych nie jest w pełni i formalnie opisany. Nie pokazano żadnych złożonych implementacji na pełną skalę. Musisz je „wyczuć” i wdrożyć w sposób, który najlepiej pasuje do Twojego stylu i potrzeb. Python to świetny język i daje Ci całą moc potrzebną do tworzenia elastycznego i wielokrotnego użytku kodu.

Daje ci jednak więcej. Daje „wolność” pisania naprawdę złego kodu. Nie rób tego! Nie powtarzaj się (DRY) i nigdy nie pisz linii kodu dłuższych niż 80 znaków. I nie zapomnij użyć wzorców projektowych tam, gdzie ma to zastosowanie; to jeden z najlepszych sposobów uczenia się od innych i bezpłatnego korzystania z ich bogatego doświadczenia.