为移动中的程序员提供的自助有限状态机。
项目描述
Automat
为移动中的程序员提供的自助有限状态机。
Automat是一个用于简洁、直观地用Python表达有限状态自动机(尤其是确定有限状态转换器)的库。
在此处阅读更多,或在Read the Docs上阅读,或观看以下视频以获取概述和演示
为什么使用状态机?
有时您必须创建一个其行为随其状态变化的对象,但仍然希望向其调用者呈现一致的接口。
例如,假设你正在编写咖啡机的软件。它有一个可以打开或关闭的盖子,一个水仓,一个咖啡豆仓,以及一个“冲泡”按钮。
咖啡机可能有多种状态。它可能没有水或可能有水。它可能没有咖啡豆或可能有咖啡豆。盖子可能是打开的或关闭的。只有在这其中一种配置下,“冲泡”按钮才应该尝试冲泡咖啡,而“打开盖子”按钮只有在咖啡实际上没有在冲泡时才能工作。
通过勤奋和注重细节,你可以使用对象上的属性集来正确地实现这一点;例如 hasWater
、hasBeans
、isLidOpen
等。但是,你必须保持所有这些属性的一致性。随着咖啡机变得越来越复杂——比如你可能添加一个用于调味料的额外仓以制作榛子咖啡——你必须不断添加更多检查和更多关于哪些状态组合是被允许的推理。
与其在每个方法中添加繁琐的 if
检查以确保每个标志都正好是你期望的,你不如使用状态机来确保如果代码运行,它将使用所有必需的初始化值运行,因为这些值必须按照你声明的顺序调用。
你可以在 Jean-Paul Calderone 的这篇优秀的文章中详细了解状态机和它们对 Python 程序员的优势:https://web.archive.org/web/20160507053658/https://clusterhq.com/2013/12/05/what-is-a-state-machine/。
Automat有什么不同?
PyPI上有数十个库实现状态机。因此,有必要说明为什么还需要另一个。
Automat的设计原则是:虽然围绕状态机组织代码是个好主意,但调用者不必,也不应该关心你是否这样做了。在 Python 中,有状态系统的“输入”是方法调用;如果需要调用副作用,则“输出”可能是方法调用,如果只是在内存中执行计算,则可能是返回值。大多数其他状态机库要求你显式创建一个输入对象,将该对象提供给通用的“输入”方法,然后接收结果,有时是用该库的接口,有时是用你自己定义的类。
例如,上面的咖啡机示例片段可以在纯 Python 中这样实现:
class CoffeeMachine(object):
def brewButton(self) -> None:
if self.hasWater and self.hasBeans and not self.isLidOpen:
self.heatTheHeatingElement()
# ...
使用 Automat,你将从描述所有输入的 typing.Protocol
开始
from typing import Protocol
class CoffeeBrewer(Protocol):
def brewButton(self) -> None:
"The user pressed the 'brew' button."
def putInBeans(self) -> None:
"The user put in some beans."
然后我们需要一个具体的类来包含不同状态之间共享的核心状态
from dataclasses import dataclass
@dataclass
class BrewerCore:
heatingElement: HeatingElement
接下来,我们需要描述我们的状态机,包括所有我们的状态。为了简单起见,让我们假设只有两种状态:noBeans
和 haveBeans
from automat import TypeMachineBuilder
builder = TypeMachineBuilder(CoffeeBrewer, BrewerCore)
noBeans = builder.state("noBeans")
haveBeans = builder.state("haveBeans")
接下来,我们可以描述一个简单的转换;当我们放入咖啡豆时,我们移动到 haveBeans
状态,没有其他行为。
# When we don't have beans, upon putting in beans, we will then have beans
noBeans.upon(CoffeeBrewer.putInBeans).to(haveBeans).returns(None)
然后我们可以用装饰器描述另一个转换,这个转换确实有一些行为,需要加热加热元件来冲泡咖啡
@haveBeans.upon(CoffeeBrewer.brewButton).to(noBeans)
def heatUp(inputs: CoffeeBrewer, core: BrewerCore) -> None:
"""
When we have beans, upon pressing the brew button, we will then not have
beans any more (as they have been entered into the brewing chamber) and
our output will be heating the heating element.
"""
print("Brewing the coffee...")
core.heatingElement.turnOn()
然后通过构建状态机来最终确定状态机,这给我们一个可调用的对象,它接受一个 BrewerCore
并返回一个合成的 CoffeeBrewer
newCoffeeMachine = builder.build()
>>> coffee = newCoffeeMachine(BrewerCore(HeatingElement()))
>>> machine.putInBeans()
>>> machine.brewButton()
Brewing the coffee...
所有的 输入 都是通过对它们像方法一样调用来提供的,所有 输出行为 都会在根据 upon
指定的输出产生时自动调用,所有状态都只是简单的透明令牌。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。
源分发
构建分发
automat-24.8.1.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | b34227cf63f6325b8ad2399ede780675083e439b20c323d376373d8ee6306d88 |
|
MD5 | c023f8848366053a76f99cffba3ea34e |
|
BLAKE2b-256 | 8d2dede4ad7fc34ab4482389fa3369d304f2fa22e50770af706678f6a332fa82 |
Automat-24.8.1-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | bf029a7bc3da1e2c24da2343e7598affaa9f10bf0ab63ff808566ce90551e02a |
|
MD5 | d86cdc12efaf8a933b1e49a24f208f7a |
|
BLAKE2b-256 | afcc55a32a2c98022d88812b5986d2a92c4ff3ee087e83b712ebc703bba452bf |