paint-brush
Python에서 상태 패턴을 사용하여 무한히 긴 에세이 작성~에 의해@aayn
2,351 판독값
2,351 판독값

Python에서 상태 패턴을 사용하여 무한히 긴 에세이 작성

~에 의해 Aayush10m2023/12/27
Read on Terminal Reader

너무 오래; 읽다

상태 디자인 패턴은 객체 지향 코드의 상태 머신입니다. 우리는 이 패턴을 사용하여 에세이/문장 생성기를 만듭니다.
featured image - Python에서 상태 패턴을 사용하여 무한히 긴 에세이 작성
Aayush HackerNoon profile picture
0-item

무한히 긴 에세이를 써보자! 그러나 그것은 불가능한 일이다. 그러나 영원히 실행되면 무한히 긴 에세이를 생성하는 프로세스를 만들 수 있습니다. 충분히 가깝습니다 .


이제 한 줄의 Python 코드로 길고 반복적인 에세이를 생성할 수 있습니다.


 >>> "This is water. " * 20 'This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. '


하아 ... 야유! 대신 이 기사에서는 상태 디자인 패턴을 사용하여 훨씬 더 흥미로운 에세이를 생성할 것입니다.


먼저 상태 머신이 무엇인지, 상태 디자인 패턴과 어떤 관련이 있는지 이해하겠습니다. 다음으로, (합리적으로) 흥미롭고 무한한 에세이를 생성할 수 있는 상태 머신을 만들겠습니다. 그런 다음 상태 디자인 패턴에 대해 간략하게 살펴보겠습니다. 마지막으로 상태 디자인 패턴을 사용하여 해당 상태 머신을 객체 지향 코드로 변환합니다.


소프트웨어 디자인 패턴은 일반적으로 발생하는 문제를 해결하는 효과적인 방법입니다. 상태 패턴과 같은 소프트웨어 디자인 패턴을 적절하게 적용하면 더 나은 확장성, 유지 관리 및 테스트 가능한 소프트웨어를 작성하는 데 도움이 될 수 있습니다.

상태 머신

본질적으로 상태 디자인 패턴은 상태 머신을 객체 지향 코드로 변환합니다.


상태 머신에 익숙하지 않다면 이는 매우 간단한 개념입니다. 상태 기계에는 상태전환이 있습니다. 상태는 관심 시스템의 특정 속성이며 상태 전환은 이러한 속성을 변경하여 상태 변경을 유발하는 작업입니다.


나는 무엇보다도 로봇 공학에 대한 배경 지식을 가지고 있고 상태 기계가 로봇 공학에서 광범위하게 사용되기 때문에 상태 기계가 어떻게 작동하는지 설명하기 위해 로봇 진공 청소기의 간단한 예를 사용하겠습니다.


로봇 진공청소기를 구동하는 상태 머신.


상태 머신 다이어그램은 상태 머신을 본 적이 없더라도 로봇이 어떻게 작동하는지 직관적으로 보여줍니다. 이 작업을 단계별로 살펴보겠습니다.


  1. 로봇은 Docked 상태에서 시작합니다(검은색 점은 시작 상태를 나타냄).
  2. 로봇이 배터리 부족을 감지하면 배터리가 가득 찰 때까지 자체 충전( 충전 상태)을 시작합니다. 배터리가 가득 차면 도킹 상태로 돌아갑니다.
  3. Docked 상태에서 로봇은 바닥이 더러워진 것을 감지하면(배터리가 부족하지 않음) 바닥 청소를 시작합니다( Clean 상태).
  4. 청소 상태에서 로봇의 배터리가 부족해지면 스스로 충전됩니다. 그리고 바닥이 깨끗하면 로봇은 도크로 돌아갑니다( 도킹된 상태).


따라서 우리 로봇 진공청소기에는 도킹됨 , 청소됨 , 충전됨 의 세 가지 상태가 있으며 바닥과 배터리의 감각 감지를 기반으로 전환됩니다.


무한 에세이를 위한 간단한 상태 머신

이제 기본 수준에서 상태 머신을 이해했으므로 무한한 에세이를 작성할 수 있는 상태 머신을 만들어 보겠습니다.


무한 에세이를 생성하는 상태 머신 - 버전 1


위는 영어 문법을 사용하여 짧고 간단한 문장으로 구성된 에세이를 생성하는 상태 머신입니다. 조만간 더 흥미로운 버전을 선보이겠다고 약속합니다. 하지만 이는 이해를 위한 좋은 출발점이 될 것입니다. 어떻게 작동하는지 살펴보겠습니다.


  1. 명사 상태부터 시작하여 미리 정의된 명사 목록에서 선택하여 명사를 생성합니다. 우리의 명사가 "The World"라고 가정 해 봅시다. (지금까지의 문장: “The World”)
  2. 그런 다음 동사 상태가 되어 다음 동사(예: “짖는다”)를 생성합니다. (지금까지의 문장 : "세계가 짖는다")
  3. 형용사 상태에서 형용사(예: "빨간색")를 생성합니다. (지금까지의 문장: “세상은 붉게 짖는다”)
  4. 그런 다음 Endmark 상태에서 "!"라는 종료 구두점 중 하나를 생성합니다. (문장: “세상이 붉게 짖는다!”)
  5. 마지막으로 에세이의 다음 문장을 생성하기 위해 명사 상태로 돌아갑니다.


이 상태 머신은 다음과 같은 (말도 안되는) 에세이를 생성할 수 있습니다.


세상이 붉게 짖는다! 사촌 해리가 파울을 했다고요? 호랑이는 반짝반짝 재미있습니다. …


무한 에세이를 위한 비결정적 상태 머신

"비결정적"이라는 말은 복잡하게 들리지만, 우리의 목적에 있어서는 단지 임의성을 추가하는 것을 의미합니다. 기본적으로 우리는 일부 상태로 전환하기 전에 일종의 동전 던지기 기능을 추가하고 있습니다. 내가 무슨 뜻인지 알게 될 것이다.

무한한 에세이를 생성하는 비결정론적 상태 머신 - 버전 2


위의 비결정적 상태 머신은 이전 상태 머신과 매우 유사합니다. 유일한 차이점은 다음과 같습니다.

  • 부정은 "아니오" 또는 "아님"과 같은 단어이고 접속사는 "그리고" 및 "그러나"와 같은 단어입니다.
  • 동사 상태에서는 동사를 생성한 다음 동전을 던집니다. 앞면이 나오면(확률 50%) 부정 상태로 전환됩니다. 그렇지 않으면 형용사 상태로 이동합니다.
  • 마찬가지로 형용사 상태에서는 형용사를 생성한 다음 동전을 던집니다. 앞면이 나오면 결합 상태로 이동합니다. 뒷면이면 Endmark 상태로 이동합니다.


무작위성, 부정 및 접속사의 도입으로 이제 더 흥미롭고 가변 길이의 문장을 생성할 수 있습니다.


상태 디자인 패턴

이제 상태 디자인 패턴이 어떻게 작동하는지 이해해 보겠습니다. 다시 한 번 말하지만, 우리는 상태 기계를 객체 지향 코드로 변환하려고 한다는 점을 기억하세요.


에세이 생성 상태 머신에서 모든 상태는 두 가지 작업을 수행해야 한다는 점을 관찰하세요.

  1. 몇 가지 작업을 수행합니다. 이 경우 단어(명사, 형용사 등)를 생성합니다.
  2. 다음 상태로 전환합니다. 명사 부터 동사 까지.


특정 국가의 관점에서 보면 그 외에는 알아야 할 일이나 해야 할 일이 없습니다 . 전체 시스템(모든 상태 및 전환)의 복잡성으로 인해 수렁에 빠지는 대신 한 번에 하나의 상태에만 집중할 수 있습니다. 내 생각에는 이런 종류의 격리분리가 State 패턴의 가장 큰 판매 포인트입니다.


아래에는 상태 디자인 패턴에 대한 UML 다이어그램이 있습니다. Context 클래스로 설명되는 각 상태가 작동하는 일부 컨텍스트가 있습니다. 컨텍스트 객체에는 작업을 수행하기 위해 현재 상태를 호출하는 데 사용하는 개인 상태 속성이 있습니다. 각 상태는 해당 작업을 수행하고 다음 상태를 반환하는 메서드가 포함된 State 인터페이스를 구현합니다.


상태 디자인 패턴 UML 다이어그램


이를 에세이 생성 예제에 매핑하면 UML 다이어그램은 다음과 같습니다.


에세이 생성을 위해 구현된 상태 패턴


WordState 는 이제 인터페이스가 아닌 추상 클래스(기울임꼴로 표시)입니다. 추상 클래스에는 일부 추상(구현되지 않은) 메서드와 특성이 있을 수 있고 다른 클래스는 정의될 수 있습니다. 인터페이스는 완전히 추상적입니다. 인터페이스의 모든 메서드는 추상적입니다. generateWord 구현이 모든 상태에서 동일하고 중복 코드를 피하는 것이 좋기 때문에 이렇게 변경했습니다.


위의 각 속성과 메소드를 분석해 보겠습니다. EssayContext 클래스에는 다음이 포함됩니다.

  • state : 현재 WordState 개체에 대한 참조입니다.
  • essayBody : 지금까지 생성된 모든 단어의 목록입니다.
  • setState() : state 속성을 변경하는 Setter입니다.
  • addWord() : 에세이 본문에 다음 단어를 추가하는 메소드입니다.
  • generateEssay() : 에세이를 생성하기 위해 이 메소드를 호출합니다. essayBody 길이가 length 보다 길면 중지합니다.
  • printEssay() : 생성된 에세이의 문자열을 반환합니다.


추상 클래스 WordState 에는 다음이 있습니다.

  • wordList : 생성할 단어를 선택하는 단어 목록에 대한 추상 속성(기울임꼴로 표시)입니다.
  • generateWord() : 생성된 단어를 에세이 컨텍스트에 추가하는 메서드입니다.
  • nextState() : 다음 상태를 반환하기 위한 추상 메서드입니다.


WordState 에서 상속된 다른 모든 구체적인 상태에 대한 대표적인 예로 NounState 사용하겠습니다.

  • wordList : 생성할 단어를 선택하는 명사의 목록입니다.
  • nextState() : 다음 상태를 반환합니다.


이제 실제로 이를 코드로 구현하는 데 필요한 모든 것이 준비되었습니다. 계속해서 그렇게 해보자!


파이썬 코드

먼저 essay_context.py 라는 파일에 EssayContext 클래스를 작성해 보겠습니다. Camel Case를 버리고 Snake Case로 전환하겠습니다. 왜냐하면 Python은... 뱀이기 때문입니다(죄송합니다).


 from word_state import WordState class EssayContext: def __init__(self, state: WordState): self.state = state self.essay_body: list[str] = [] def set_state(self, state: WordState): self.state = state def add_word(self, word: str): self.essay_body.append(word) def generate_essay(self, length: int): while len(self.essay_body) < length: self.state.generate_word(self) def print_essay(self) -> str: return " ".join(self.essay_body)


그런 다음 word_state.py 라는 파일에 상태를 추가해 보겠습니다.


 import abc import numpy as np class WordState(abc.ABC): word_list: list[str] @classmethod def generate_word(cls, context: "EssayContext"): word = np.random.choice(cls.word_list) context.add_word(word) context.set_state(cls.next_state()) @classmethod @abc.abstractmethod def next_state(cls) -> "WordState": pass class NounState(WordState): word_list: list[str] = ["everything", "nothing"] @classmethod def next_state(cls): return VerbState class VerbState(WordState): word_list: list[str] = ["is", "was", "will be"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return NegationState return AdjectiveState class NegationState(WordState): word_list: list[str] = ["not"] @classmethod def next_state(cls): return AdjectiveState class AdjectiveState(WordState): word_list: list[str] = ["fantastic", "terrible"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return ConjunctionState return EndmarkState class ConjunctionState(WordState): word_list: list[str] = ["and", "but"] @classmethod def next_state(cls): return NounState class EndmarkState(WordState): word_list: list[str] = [".", "!"] @classmethod def next_state(cls): return NounState


마지막으로 main.py 의 모든 것을 실행하는 코드를 추가해 보겠습니다.


 from essay_context import EssayContext from word_state import NounState if __name__ == '__main__': ctx = EssayContext(NounState) ctx.generate_essay(100) print(ctx.print_essay())


python main.py 실행하면 다음과 같은 출력이 제공됩니다(비결정성으로 인해 매번 다름).


 'everything is not terrible and nothing was terrible ! everything will be not fantastic but everything is fantastic . everything will be fantastic . nothing will be fantastic and nothing will be terrible ! everything was not fantastic and everything will be not terrible . everything was terrible . nothing was terrible but nothing will be fantastic ! nothing is not terrible . nothing was not fantastic but everything was not fantastic ! everything will be not fantastic but everything will be terrible ! everything will be not fantastic . everything is fantastic but nothing will be not terrible ! everything will be not fantastic but nothing was not fantastic !'


이런 간단한 시스템도 나쁘지 않네요! 또한 다양한 단어 목록을 확장하거나 더 많은 상태를 추가하여 에세이 생성을 더욱 정교하게 만들 수도 있습니다. 에세이를 한 단계 더 발전시키기 위해 일부 LLM API를 도입할 수도 있습니다.


마지막 생각들

상태 머신과 상태 패턴은 "상태"에 대한 잘 정의된 개념을 사용하여 시스템을 모델링하고 생성하는 데 매우 적합합니다. 즉, 각 상태와 관련된 특정 동작과 속성이 있습니다. 로봇 진공청소기가 청소, 도킹 또는 충전 중입니다. TV는 켜짐 또는 꺼짐일 수 있으며, TV 리모컨 버튼은 TV 상태에 따라 다르게 작동합니다.


또한 잘 정의된 패턴으로 시퀀스를 생성하거나 식별하는 데에도 적합합니다. 이는 에세이 생성 예제에 적용됩니다.


마지막으로, “이게 다 무슨 의미가 있나요?”라고 물을 수도 있습니다. 왜 우리는 이 "무한" 에세이를 생성하기 위해 다양한 주와 클래스를 정의하는 데 어려움을 겪었습니까? 동일한 동작을 달성하기 위해 20줄(또는 그 이하)의 Python 코드를 작성할 수도 있었습니다.


짧은 대답은 더 나은 확장성을 위한 것입니다.


단지 3~5개의 주가 아니라 50~500개의 주가 있다고 상상해 보세요. 이것은 과장된 표현이 아닙니다. 실제 엔터프라이즈 시스템에는 그 정도 수준의 복잡성이 있습니다. 갑자기 State 패턴은 분리와 격리로 인해 훨씬 더 매력적으로 보입니다. 전체 시스템을 머릿속에 담아둘 필요 없이 한 번에 하나의 상태에만 집중할 수 있습니다. 한 상태가 다른 상태에 영향을 주지 않으므로 변경 사항을 도입하는 것이 더 쉽습니다. 또한 확장 가능하고 유지 관리 가능한 시스템의 큰 부분인 단위 테스트를 더 쉽게 할 수 있습니다.


궁극적으로 State 패턴은 단지 상태 관리에 관한 것이 아닙니다. 모든 디자인 패턴과 마찬가지로 이는 정교한 만큼 확장성과 유지 관리가 가능한 시스템 구축을 위한 청사진입니다.