paint-brush
在 Python 中使用状态模式写一篇无限长的论文经过@aayn
2,426 讀數
2,426 讀數

在 Python 中使用状态模式写一篇无限长的论文

经过 Aayush Naik10m2023/12/27
Read on Terminal Reader

太長; 讀書

状态设计模式是面向对象代码中的状态机。我们使用该模式来创建文章/句子生成器。
featured image - 在 Python 中使用状态模式写一篇无限长的论文
Aayush Naik 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. 对接状态下,如果机器人检测到地板脏了(且电池电量不低),则开始清洁地板(清洁状态)。
  4. 清洁状态下,如果机器人电量不足,它会自行充电。如果地板干净,机器人将返回其停靠位置(停靠状态)。


因此,我们的机器人真空吸尘器具有三种状态:对接清洁充电,并根据地板及其电池的感官检测进行转换。


无限论文的简单状态机

现在我们已经基本了解了状态机,让我们创建一个能够编写无限论文的状态机。


状态机生成无限论文 - 版本 1


上面是一个状态机,它使用英语语法生成由简短句子组成的文章。我保证我们很快就会得到一个更有趣的版本,但这应该作为理解的一个很好的起点。让我们看一下它是如何工作的。


  1. 名词状态开始,我们通过从一些预定义的名词列表中进行选择来生成名词。假设我们的名词是“世界”。 (到目前为止的句子:“世界”)
  2. 然后我们最终进入动词状态,接下来生成一个动词(比如“吠叫”)。 (到目前为止的句子:“世界在咆哮”)
  3. 我们在形容词状态下生成一个形容词(例如“红色”)。 (到目前为止的句子:“世界咆哮红色”)
  4. 然后,在Endmark状态下,我们生成终止标点符号之一,例如“!”。 (句子:“世界咆哮红色!”)
  5. 最后,我们回到名词状态来生成文章中的下一个句子。


这个状态机可能会生成一篇如下所示的(无意义的)文章。


世界变成红色!哈里表弟下雨了?老虎闪闪发光,很有趣。 ……


无限论文的非确定性状态机

尽管“非确定性”听起来很复杂,但就我们的目的而言,它只是意味着添加一些随机性。本质上,我们在过渡到某些状态之前添加了一种抛硬币的方法。你就会明白我的意思了。

生成无限论文的非确定性状态机 - 版本 2


上面的非确定性状态机与之前的非常相似。唯一的区别是:

  • 否定词是“no”或“not”,连词是“and”和“but”等词。
  • 动词状态下,我们生成一个动词,然后扔一枚硬币。如果正面朝上(50% 的概率),我们将进入否定状态;否则我们就进入形容词状态。
  • 类似地,在形容词状态下,我们生成一个形容词,然后扔一枚硬币。如果是正面,我们就进入合相状态;如果是反面,则进入Endmark状态。


通过引入随机性、否定性和连词,我们现在可以生成更有趣且长度可变的句子。


状态设计模式

现在,让我们了解状态设计模式是如何工作的。再次记住,我们正在尝试将状态机转换为面向对象的代码。


在论文生成状态机中,观察到每个状态都需要做两件事。

  1. 执行一些操作。在本例中,生成一个单词(名词、形容词等)。
  2. 过渡到下一个状态。从名词动词,等等。


从特定国家的角度来看,它不需要了解或做任何其他事情。我们可以一次只关注一种状态,而不是被整个系统的复杂性(所有状态和转换)所困扰。在我看来,这种隔离解耦是State模式的最大卖点。


下面是状态设计模式的UML图。每个状态都在一定的上下文中运行,如Context类所示。上下文对象有一个私有状态属性,它用它来调用当前状态来执行其操作。每个状态都实现一个State接口,其中包含执行其动作或操作并返回下一个状态的方法。


状态设计模式UML图


如果我们将其映射到论文生成示例上,UML 图如下所示。


用于论文生成的状态模式


WordState现在是一个抽象类(用斜体表示)而不是一个接口。抽象类可以有一些抽象的(未实现的)方法和属性,而其他的可以定义。接口是完全抽象的:它们的所有方法都是抽象的。我进行此更改是因为所有状态下的generateWord实现都是相同的,并且避免重复代码是件好事。


让我们分解一下上面的每个属性和方法。在EssayContext类中,我们有:

  • state :对当前WordState对象的引用。
  • essayBody :到目前为止生成的所有单词的列表。
  • setState() :用于更改state属性的 Setter。
  • addWord() :将下一个单词添加到文章正文的方法。
  • generateEssay() :我们调用这个方法来生成我们的文章;当essayBody的 length 大于length时我们停止。
  • printEssay() :返回生成的论文的字符串。


在抽象类WordState中,我们有:

  • wordList :我们从中选择要生成的单词列表的抽象属性(以斜体表示)。
  • generateWord() :将生成的单词添加到文章上下文中的方法。
  • nextState() :返回下一个状态的抽象方法。


我们将使用NounState作为从WordState继承的所有其他具体状态的代表性示例。

  • wordList :我们从中选择要生成的单词的名词列表。
  • nextState() :返回下一个状态。


现在,我们已经拥有了在代码中实际实现这一点所需的一切。让我们继续这样做吧!


Python代码

首先,我们在名为essay_context.py的文件中编写EssayContext类。我们将放弃驼峰式命名法并改用蛇式命名法,因为,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 来将我们的论文提升到一个新的水平。


最后的想法

状态机和状态模式非常适合建模和创建具有明确定义的“状态”概念的系统。也就是说,每个状态都有特定的行为和属性。扫地机器人正在清洁、对接或充电。您的电视可以打开或关闭,并且电视遥控器按钮的作用会根据电视的状态而有所不同。


它也非常适合生成或识别具有明确定义模式的序列。这适用于我们的论文生成示例。


最后,您可能会问:“这一切有什么意义?”为什么我们要费尽心思定义各种状态和类别来生成这篇“无限”的文章?我们可以编写 20 行(或更少)Python 代码来实现相同的行为。


简而言之,答案是为了更好的可扩展性


想象一下,如果我们不再只有三五个州,而是有 50 个或 500 个州。这并非夸张;真正的企业系统确实具有这种程度的复杂性。突然间,状态模式由于其解耦和隔离而显得更具吸引力。我们可以一次只关注一种状态,而不必将整个系统保留在我们的脑海中。引入更改更容易,因为一种状态不会影响其他状态。它还允许更轻松的单元测试,这是可扩展和可维护系统的重要组成部分。


最终,状态模式不仅仅是管理状态;它还涉及管理状态。与所有设计模式一样,它是构建既复杂又可扩展、可维护的系统的蓝图。