Python 디자인 패턴: 세련되고 세련된 코드를 위해
게시 됨: 2022-03-11다시 한 번 말해보자. Python은 동적 유형 지정 및 동적 바인딩을 사용하는 고급 프로그래밍 언어입니다. 강력하고 수준 높은 동적 언어로 설명하겠습니다. 많은 개발자가 Python의 명확한 구문, 잘 구조화된 모듈 및 패키지, 엄청난 유연성과 다양한 최신 기능으로 인해 Python을 사랑합니다.
Python에서는 클래스를 작성하고 클래스에서 객체를 인스턴스화해야 하는 것은 없습니다. 프로젝트에 복잡한 구조가 필요하지 않은 경우 함수를 작성하면 됩니다. 더 좋은 점은 코드를 전혀 구조화하지 않고도 간단하고 빠른 작업을 실행하기 위한 플랫 스크립트를 작성할 수 있다는 것입니다.
동시에 Python은 100% 객체 지향 언어입니다. 어떻게한다는거야? 간단히 말해서 파이썬의 모든 것은 객체입니다. 함수는 객체, 일급 객체(그 의미가 무엇이든 간에)입니다. 함수가 객체라는 사실은 중요하니 꼭 기억하세요.
따라서 Python으로 간단한 스크립트를 작성하거나 Python 터미널을 열고 바로 거기에서 명령문을 실행할 수 있습니다(매우 유용합니다!). 그러나 동시에 복잡한 프레임워크, 애플리케이션, 라이브러리 등을 만들 수 있습니다. 파이썬에서는 많은 것을 할 수 있습니다. 물론 여러 가지 제한 사항이 있지만 이 기사의 주제는 그것이 아닙니다.
그러나 Python은 매우 강력하고 유연하기 때문에 프로그래밍할 때 몇 가지 규칙(또는 패턴)이 필요합니다. 따라서 패턴이 무엇이며 Python과 어떤 관련이 있는지 살펴보겠습니다. 또한 몇 가지 필수 Python 디자인 패턴을 구현하는 작업을 진행할 것입니다.
Python이 패턴에 좋은 이유는 무엇입니까?
모든 프로그래밍 언어는 패턴에 좋습니다. 사실 패턴은 주어진 프로그래밍 언어의 맥락에서 고려되어야 합니다. 패턴, 언어 구문 및 특성은 모두 프로그래밍에 제한을 가합니다. 언어 구문 및 언어 특성(동적, 기능적, 객체 지향 등)에서 비롯된 제한은 다를 수 있으며 존재 이유도 마찬가지입니다. 패턴에서 오는 제한은 이유가 있고 목적이 있습니다. 이것이 패턴의 기본 목표입니다. 우리에게 무엇을 하는 방법과 하지 않는 방법을 알려줍니다. 패턴, 특히 Python 디자인 패턴에 대해서는 나중에 이야기하겠습니다.
Python의 철학은 잘 생각한 모범 사례라는 아이디어를 기반으로 합니다. Python은 동적 언어이며(이미 말했습니까?) 따라서 몇 줄의 코드로 여러 인기 있는 디자인 패턴을 이미 구현하거나 구현하기 쉽습니다. 일부 디자인 패턴은 파이썬에 내장되어 있기 때문에 우리도 모르는 사이에 사용합니다. 언어의 특성상 다른 패턴은 필요하지 않습니다.
예를 들어, Factory 는 사용자에게 인스턴스화 논리를 숨기고 새 개체를 만드는 것을 목표로 하는 구조적 Python 디자인 패턴입니다. 그러나 Python에서 객체 생성은 동적 설계이므로 Factory와 같은 추가가 필요하지 않습니다. 물론 원하는 경우 자유롭게 구현할 수 있습니다. 정말 유용한 경우가 있을 수 있지만 일반적인 경우가 아닌 예외적인 경우입니다.
파이썬 철학의 좋은 점은 무엇입니까? 이것 으로 시작하겠습니다(Python 터미널에서 탐색).
> >> 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!
이것은 전통적인 의미의 패턴이 아닐 수도 있지만 가장 우아하고 유용한 방식으로 프로그래밍에 대한 "파이썬" 접근 방식을 정의하는 규칙입니다.
코드를 구성하는 데 도움이 되는 PEP-8 코드 지침도 있습니다. 물론 몇 가지 적절한 예외를 제외하고는 저에게 필수입니다. 그건 그렇고, 이러한 예외는 PEP-8 자체에 의해 권장됩니다.
그러나 가장 중요한 것은 일관성이 없는 때를 아는 것입니다. 때로는 스타일 가이드가 적용되지 않는 경우도 있습니다. 확신이 서지 않으면 최선의 판단을 내리십시오. 다른 예를 보고 무엇이 가장 잘 보이는지 결정하십시오. 그리고 주저하지 말고 물어보세요!
PEP-8과 Zen of Python(PEP - PEP-20)을 결합하면 읽기 쉽고 유지 관리 가능한 코드를 만들기 위한 완벽한 기반을 갖게 됩니다. 디자인 패턴을 추가하면 일관성과 발전 가능성을 갖춘 모든 종류의 소프트웨어 시스템을 만들 준비가 된 것입니다.
파이썬 디자인 패턴
디자인 패턴이란?
모든 것은 GOF(Gang of Four)에서 시작됩니다. GOF에 대해 잘 모르는 경우 빠른 온라인 검색을 수행하십시오.
디자인 패턴은 잘 알려진 문제를 해결하는 일반적인 방법입니다. GOF에서 정의한 디자인 패턴의 기반에는 두 가지 주요 원칙이 있습니다.
- 구현이 아닌 인터페이스로 프로그래밍하십시오.
- 상속보다 객체 합성을 선호합니다.
파이썬 프로그래머의 관점에서 이 두 가지 원칙을 자세히 살펴보겠습니다.
구현이 아닌 인터페이스로 프로그래밍
오리 타이핑에 대해 생각해보십시오. 파이썬에서 우리는 이러한 인터페이스에 따라 인터페이스와 프로그램 클래스를 정의하는 것을 좋아하지 않습니까? 하지만, 내 말을 들어라! 이것은 우리가 인터페이스에 대해 생각하지 않는다는 것을 의미하지 않습니다. 사실 Duck Typing을 통해 우리는 항상 그렇게 합니다.
이 패러다임에 어떻게 맞는지 알아보기 위해 악명 높은 Duck Typing 접근 방식에 대해 몇 마디 말해 보겠습니다. 프로그래밍을 인터페이스로.
우리는 객체의 본질에 신경 쓰지 않고 객체가 무엇인지 신경 쓸 필요가 없습니다. 우리는 그것이 우리가 필요로 하는 것을 할 수 있는지 알고 싶을 뿐입니다(우리는 객체의 인터페이스에만 관심이 있습니다).
물체가 꽥꽥거릴 수 있습니까? 그래서, 돌이키게 놔둬!
try: bird.quack() except AttributeError: self.lol()
오리에 대한 인터페이스를 정의했습니까? 아니요! 구현 대신 인터페이스에 프로그래밍 했습니까? 네! 그리고, 나는 이것이 매우 좋다고 생각합니다.
Alex Martelli는 Python의 디자인 패턴에 대한 잘 알려진 프레젠테이션에서 "오리에게 타이핑을 가르치는 것은 시간이 걸리지만 나중에는 많은 작업을 절약할 수 있습니다!"라고 지적했습니다.
상속보다 객체 합성을 선호
이것이 제가 Pythonic 원칙이라고 부르는 것입니다! 다른 클래스에서 한 클래스(또는 더 자주, 여러 클래스)를 래핑하는 것과 비교하여 더 적은 수의 클래스/하위 클래스를 만들었습니다.
이렇게 하는 대신:
class User(DbObject): pass
다음과 같이 할 수 있습니다.
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)
장점은 분명합니다. 래핑된 클래스의 어떤 메서드를 노출할지 제한할 수 있습니다. 런타임에 지속자 인스턴스를 주입할 수 있습니다! 예를 들어, 오늘은 관계형 데이터베이스이지만 내일은 우리가 필요로 하는 인터페이스(다시 한 번 성가신 오리)가 있으면 무엇이든 될 수 있습니다.
구성은 Python에 우아하고 자연스럽습니다.
행동 패턴
행동 패턴에는 객체 간의 의사 소통, 객체가 상호 작용하고 주어진 작업을 수행하는 방법이 포함됩니다. GOF 원칙에 따르면 Python 에는 책임 사슬, 명령, 해석기, 반복기, 중재자, 메멘토, 관찰자, 상태, 전략, 템플릿, 방문자의 총 11가지 행동 패턴이 있습니다.
나는 이러한 패턴이 매우 유용하다고 생각하지만 이것이 다른 패턴 그룹이 그렇지 않다는 것을 의미하지는 않습니다.
반복자
반복자는 Python에 내장되어 있습니다. 이것은 언어의 가장 강력한 특성 중 하나입니다. 몇 년 전, 어디선가 iterator가 Python을 훌륭하게 만든다는 것을 읽었고, 지금도 마찬가지라고 생각합니다. Python 반복자와 생성기에 대해 충분히 배우면 이 특정 Python 패턴에 대해 필요한 모든 것을 알게 될 것입니다.
책임의 사슬
이 패턴은 각기 다른 방법을 사용하여 요청을 처리하는 방법을 제공합니다. 각 방법은 요청의 특정 부분을 처리합니다. 좋은 코드를 위한 최고의 원칙 중 하나는 단일 책임 원칙입니다.
모든 코드 조각은 단 하나의 일을 수행해야 합니다.
이 원칙은 이 디자인 패턴에 깊이 통합되어 있습니다.
예를 들어 일부 콘텐츠를 필터링하려는 경우 각각의 필터가 정확하고 명확하게 정의된 필터링 유형을 수행하는 다른 필터를 구현할 수 있습니다. 이러한 필터는 불쾌감을 주는 단어, 광고, 부적절한 비디오 콘텐츠 등을 필터링하는 데 사용할 수 있습니다.
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)
명령
이것은 내가 프로그래머로서 구현한 최초의 Python 디자인 패턴 중 하나입니다. 패턴은 발명된 것이 아니라 발견되는 것임을 상기시켜줍니다. 그것들은 존재합니다. 우리는 그것들을 찾아서 사용하기만 하면 됩니다. 나는 우리가 수년 전에 구현한 놀라운 프로젝트를 위해 이것을 발견했습니다: 특별한 목적의 WYSIWYM XML 편집기. 코드에서 이 패턴을 집중적으로 사용한 후 일부 사이트에서 이에 대해 더 많이 읽었습니다.
명령 패턴은 어떤 이유로 실행될 항목을 준비하고 필요할 때 실행해야 하는 상황에서 편리합니다. 이점은 이러한 방식으로 작업을 캡슐화하면 Python 개발자가 실행 취소/다시 실행 또는 작업 기록 유지 등과 같은 실행된 작업과 관련된 추가 기능을 추가할 수 있다는 것입니다.
간단하고 자주 사용되는 예제가 어떻게 생겼는지 봅시다.
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()
창조적인 패턴
생성 패턴은 Python에서 일반적으로 사용되지 않는다는 점을 지적하는 것으로 시작하겠습니다. 왜요? 언어의 동적 특성 때문입니다.
나보다 현명한 사람은 Factory가 Python에 내장되어 있다고 말했습니다. 그것은 언어 자체가 우리에게 충분히 우아한 방식으로 객체를 생성하는 데 필요한 모든 유연성을 제공한다는 것을 의미합니다. 우리는 Singleton이나 Factory와 같이 맨 위에 무엇이든 구현할 필요가 거의 없습니다.
한 Python 디자인 패턴 자습서에서 이러한 디자인이 " 새 연산자를 사용하여 직접 객체를 인스턴스화하는 대신 생성 논리를 숨기면서 객체를 생성하는 방법을 제공합니다." 라고 명시한 생성 디자인 패턴에 대한 설명을 찾았습니다.
문제를 요약하면 다음과 같습니다 . Python에는 새 연산자가 없습니다!
그럼에도 불구하고 이러한 패턴을 사용하여 이점을 얻을 수 있다고 생각되는 경우 몇 가지를 구현하는 방법을 살펴보겠습니다.
하나씩 일어나는 것
Singleton 패턴은 런타임 동안 주어진 클래스의 인스턴스가 하나만 존재하도록 보장하려는 경우에 사용됩니다. Python에서 이 패턴이 정말로 필요합니까? 내 경험에 비추어 볼 때 Singleton 패턴을 구현하는 대신 의도적으로 하나의 인스턴스를 만든 다음 사용하는 것이 더 쉽습니다.

하지만 구현하고 싶다면 좋은 소식이 있습니다. Python에서는 인스턴스화 프로세스를 변경할 수 있습니다(사실상 다른 모든 것과 함께). 앞서 언급한 __new__()
메서드를 기억하십니까? 여기 우리가 간다:
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
이 예에서 로거 는 싱글톤입니다.
다음은 Python에서 Singleton을 사용하는 것에 대한 대안입니다.
- 모듈을 사용합니다.
- 응용 프로그램의 최상위 수준, 아마도 구성 파일에 하나의 인스턴스를 만듭니다.
- 인스턴스가 필요한 모든 개체에 인스턴스를 전달합니다. 그것은 의존성 주입이며 강력하고 쉽게 마스터할 수 있는 메커니즘입니다.
의존성 주입
종속성 주입이 디자인 패턴인지 여부에 대해 논의할 생각은 없지만 느슨한 결합을 구현하는 매우 좋은 메커니즘이며 애플리케이션을 유지 관리하고 확장할 수 있도록 도와줍니다. Duck Typing과 결합하면 Force가 당신과 함께 할 것입니다. 언제나.
객체가 생성되는 시기(또는 더 나은 위치)에 대한 질문을 다루기 때문에 이 게시물의 생성 패턴 섹션에 나열했습니다. 외부에서 생성됩니다. 객체는 우리가 사용하는 곳에서 전혀 생성되지 않으므로 의존성이 소비되는 곳에서 생성되지 않는다고 말하는 것이 더 낫습니다. 소비자 코드는 외부에서 생성된 개체를 받아 사용합니다. 추가 참조를 위해 이 Stackoverflow 질문에 대한 가장 많이 투표된 답변을 읽으십시오.
이것은 의존성 주입에 대한 좋은 설명이며 우리에게 이 특정 기술의 잠재력에 대한 좋은 아이디어를 제공합니다. 기본적으로 대답은 다음 예의 문제를 설명합니다 . 냉장고에서 음료를 직접 가져오지 말고 대신 필요 사항을 명시하십시오. 부모님에게 점심과 함께 마실 것이 필요하다고 말씀하십시오.
Python은 이를 쉽게 구현하는 데 필요한 모든 것을 제공합니다. Java 및 C#과 같은 다른 언어에서 가능한 구현에 대해 생각해 보면 Python의 아름다움을 빠르게 깨닫게 될 것입니다.
종속성 주입의 간단한 예를 생각해 보겠습니다.
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)
Command 클래스에 인증 자 및 권한 부여자 메서드를 삽입합니다. Command 클래스가 필요로 하는 모든 것은 구현 세부 사항에 신경 쓰지 않고 성공적으로 실행하는 것입니다. 이런 식으로 런타임에 사용하기로 결정한 인증 및 권한 부여 메커니즘과 함께 Command 클래스를 사용할 수 있습니다.
우리는 생성자를 통해 종속성을 주입하는 방법을 보여 주었지만 개체 속성을 직접 설정하여 종속성을 쉽게 주입할 수 있으므로 더 많은 잠재력을 잠금 해제할 수 있습니다.
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)
의존성 주입에 대해 배울 것이 훨씬 더 많습니다. 예를 들어 호기심 많은 사람들은 IoC를 검색할 것입니다.
그러나 그렇게 하기 전에 이 질문에 대해 가장 많은 지지를 받은 또 다른 Stackoverflow 답변을 읽으십시오.
다시 말하지만, 우리는 Python에서 이 멋진 디자인 패턴을 구현하는 것이 언어의 내장 기능을 사용하는 문제일 뿐임을 보여주었습니다.
이 모든 것이 의미하는 바를 잊지 마십시오. 종속성 주입 기술을 사용하면 매우 유연하고 쉬운 단위 테스트가 가능합니다. 즉석에서 저장하는 데이터를 변경할 수 있는 아키텍처를 상상해 보십시오. 데이터베이스를 조롱하는 것은 간단한 작업이 되지 않습니까? 자세한 내용은 Toptal의 Python에서 Mocking 소개를 참조하세요.
Prototype , Builder 및 Factory 디자인 패턴을 연구할 수도 있습니다.
구조적 패턴
정면
이것은 아마도 가장 유명한 Python 디자인 패턴일 것입니다.
상당한 수의 개체가 있는 시스템이 있다고 상상해 보십시오. 모든 개체는 풍부한 API 메서드 집합을 제공합니다. 이 시스템으로 많은 일을 할 수 있지만 인터페이스를 단순화하는 것은 어떻습니까? 모든 API 메서드의 잘 고려된 하위 집합을 노출하는 인터페이스 개체를 추가하지 않는 이유는 무엇입니까? 외관!
Python Facade 디자인 패턴 예:
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
놀라움도 트릭도 없습니다. Car
클래스는 Facade 입니다. 그게 전부입니다.
어댑터
인터페이스 단순화를 위해 Facades 가 사용되는 경우 어댑터 는 인터페이스 변경에 관한 것입니다. 시스템이 오리를 기다리고 있을 때 소를 사용하는 것과 같습니다.
주어진 목적지에 정보를 기록하는 작업 방법이 있다고 가정해 보겠습니다. 귀하의 메소드는 대상이 write()
메소드를 가질 것으로 예상합니다(예를 들어 모든 파일 객체가 가지고 있는 것처럼).
def log(message, destination): destination.write('[{}] - {}'.format(datetime.now(), message))
나는 그것이 의존성 주입을 통해 잘 작성된 방법이라고 말하고 싶습니다. 이는 뛰어난 확장성을 허용합니다. 파일 대신 일부 UDP 소켓에 기록하고 싶다면 이 UDP 소켓을 여는 방법을 알고 있지만 유일한 문제는 socket
개체에 write()
메서드가 없다는 것입니다. 어댑터 가 필요합니다!
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)
그런데 왜 어댑터 가 그렇게 중요하다고 생각합니까? 의존성 주입과 효과적으로 결합될 때 엄청난 유연성을 제공합니다. 새 인터페이스를 잘 알려진 인터페이스로 변환하는 어댑터를 구현할 수 있는데도 잘 테스트된 코드를 변경하여 새 인터페이스를 지원하는 이유는 무엇입니까?
또한 어댑터 와 유사하기 때문에 브리지 및 프록시 디자인 패턴을 확인하고 마스터해야 합니다. Python에서 구현하기가 얼마나 쉬운지 생각하고 프로젝트에서 사용할 수 있는 다양한 방법에 대해 생각하십시오.
데코레이터
오 우리는 얼마나 운이 좋은가! 데코레이터 는 정말 훌륭하고 이미 언어에 통합되어 있습니다. 내가 Python에서 가장 좋아하는 것은 그것을 사용하는 것이 우리에게 모범 사례를 사용하도록 가르친다는 것입니다. 우리가 모범 사례(특히 디자인 패턴)에 대해 의식할 필요가 없다는 것은 아니지만 Python을 사용하면 상관없이 모범 사례를 따르는 것처럼 느낍니다. 개인적으로 Python 모범 사례는 직관적이고 제2의 천성이며 이는 초보자와 엘리트 개발자 모두에게 높이 평가되는 것입니다.
데코레이터 패턴은 추가 기능을 도입하는 것, 특히 상속을 사용하지 않고 수행하는 것입니다.
내장된 Python 기능을 사용하지 않고 메서드를 장식하는 방법을 살펴보겠습니다. 다음은 간단한 예입니다.
def execute(user, action): self.authenticate(user) self.authorize(user, action) return action()
여기서 그다지 좋지 않은 것은 execute
기능이 무언가를 실행하는 것보다 훨씬 더 많은 일을 한다는 것입니다. 우리는 서신에 단일 책임 원칙을 따르지 않습니다.
다음과 같이 간단하게 작성하는 것이 좋습니다.
def execute(action): return action()
다음과 같이 데코레이터 의 다른 위치에서 모든 권한 부여 및 인증 기능을 구현할 수 있습니다.
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)
이제 execute()
메서드는 다음과 같습니다.
- 읽기 쉬운
- 한 가지만 합니다(적어도 코드를 볼 때)
- 인증으로 장식되어 있습니다
- 인증으로 장식되어 있습니다
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()
데코레이터로서의 기능에 국한되지 않는다는 점에 유의하는 것이 중요합니다. 데코레이터는 전체 클래스를 포함할 수 있습니다. 유일한 요구 사항은 호출 가능 해야 한다는 것입니다. 그러나 우리는 그것에 대해 아무런 문제가 없습니다. __call__(self)
메서드를 정의하기만 하면 됩니다.
Python의 functools 모듈을 자세히 살펴보고 싶을 수도 있습니다. 거기에는 발견할 것이 많습니다!
결론
Python의 디자인 패턴을 사용하는 것이 얼마나 자연스럽고 쉬운지 보여줬지만 Python 프로그래밍도 쉽게 진행되어야 하는 방법도 보여주었습니다.
"복잡한 것보다 단순한 것이 낫다" 는 말을 기억하십니까? 디자인 패턴 중 어느 것도 완전하고 형식적으로 설명되어 있지 않다는 것을 눈치채셨을 것입니다. 복잡한 전체 규모 구현은 표시되지 않았습니다. 당신의 스타일과 필요에 가장 잘 맞는 방식으로 그것들을 "느끼고" 구현해야 합니다. Python은 훌륭한 언어이며 유연하고 재사용 가능한 코드를 생성하는 데 필요한 모든 기능을 제공합니다.
그러나 그 이상을 제공합니다. 정말 나쁜 코드를 작성할 수 있는 "자유"를 제공합니다. 하지마! Don't Repeat Yourself(DRY) 및 80자보다 긴 코드 라인을 작성하지 마십시오. 그리고 해당되는 경우 디자인 패턴을 사용하는 것을 잊지 마십시오. 그것은 다른 사람들로부터 배우고 그들의 풍부한 경험을 무료로 얻을 수 있는 가장 좋은 방법 중 하나입니다.