python状态机transitions库详解

yizhihongxing

Python状态机transitions库详解

概述

状态机(State Machine)是计算机科学里的基础概念,它描述了物体可能的所有状态,在不同的事件或条件下,物体状态会发生相应的改变。在编程中,状态机可以应用到许多场景中,比如游戏状态切换、流程控制等。Python状态机transitions库是一个非常优秀的、易于使用的状态机库,本文将详细介绍该库的使用。

安装

transitions库可以通过pip安装:

pip install transitions

建立状态机

在transitions中,我们通过定义不同状态和状态间的转移来建立状态机。比如我们有一个简单的场景,要实现一个电视机的开关:

from transitions import Machine

class TV(object):
    # 定义状态
    states = ['off', 'on']

    def __init__(self):
        self.machine = Machine(model=self, states=TV.states, initial='off')
        # 定义状态转移
        self.machine.add_transition('power_on', 'off', 'on')
        self.machine.add_transition('power_off', 'on', 'off')

首先我们定义了一个TV类,它有两种状态:off和on。我们通过Machine类来创建状态机对象,将其绑定到我们的类中。initial参数指定了初始状态,这里我们将其设为off。接着我们定义了两个方法,power_on和power_off,分别用来实现开机和关机。

接下来,我们可以创建一个TV对象并测试:

t = TV()
print(t.state) # off
t.power_on()
print(t.state) # on
t.power_off()
print(t.state) # off

状态转移回调函数

我们也可以通过状态转移回调函数来实现复杂的状态转移,例如我们想让电视在关机时弹出确认对话框,在开机时显示欢迎词,我们可以这么做:

from transitions import Machine

class TV(object):
    # 定义状态
    states = ['off', 'on']

    def __init__(self):
        self.machine = Machine(model=self, states=TV.states, initial='off')
        # 定义状态转移
        self.machine.add_transition('power_on', 'off', 'on', before='say_hello')
        self.machine.add_transition('power_off', 'on', 'off', before='check_shutdown')

    # 开机回调函数
    def say_hello(self):
        print('欢迎使用电视机!')

    # 关机回调函数
    def check_shutdown(self):
        input_str = input('您确定要关闭电视机吗?(y/n)')
        if input_str.lower() == 'y':
            print('正在关闭电视机...')
        else:
            print('取消关闭。')

t = TV()
t.power_on()
t.power_off()

通过before参数,我们可以在状态转移前调用指定的回调函数。这里我们在开机时调用了say_hello函数,它将输出欢迎词;在关机时调用了check_shutdown函数,它将显示一个确认对话框,让用户决定是否关闭电视机。

示例一:闹钟

我们来看一个更实际的例子,假设我们要实现一个闹钟程序,它有以下四种状态:未设置、已设置、已响铃未关闭、已响铃已关闭。根据这些状态,我们需要实现以下一些操作:

  • set_alarm():设置闹钟时间;
  • snooze():将闹钟延迟10分钟;
  • alarm_on():开启闹钟;
  • alarm_off():关闭闹钟;
  • alarm_stop():关闭闹钟铃声。

首先我们需要定义闹钟:

from datetime import datetime, timedelta
from transitions import Machine

class Alarm(object):
    # 定义状态
    states = ['not_set', 'set', 'ringing', 'halted']

    def __init__(self):
        self.machine = Machine(model=self, states=Alarm.states, initial='not_set')
        # 定义状态转移
        self.machine.add_transition('set_alarm', 'not_set', 'set')
        self.machine.add_transition('alarm_on', 'set', 'ringing')
        # snooze操作可以在已响铃未关闭、已响铃已关闭状态下使用
        self.machine.add_transition('snooze', ['ringing', 'halted'], 'ringing', before='delay_alarm')
        self.machine.add_transition('alarm_off', '*', 'halted')
        self.machine.add_transition('alarm_stop', 'ringing', 'halted', before='stop_ring')

    # 处理snooze
    def delay_alarm(self):
        self.alarm_time = datetime.now() + timedelta(minutes=10)
        print('已将闹钟延迟至{}时{}分\n'.format(self.alarm_time.hour, self.alarm_time.minute))

    # 处理alarm_stop
    def stop_ring(self):
        print('闹钟已关闭铃声')

    def __str__(self):
        if self.state == 'not_set':
            return '闹钟未设置'
        elif self.state == 'set':
            return '闹钟已设置,时间为{}时{}分'.format(self.alarm_time.hour, self.alarm_time.minute)
        elif self.state == 'ringing':
            return '闹钟响铃中...'
        elif self.state == 'halted':
            return '闹钟已关闭'

我们定义了一个Alarm类,有四种状态:not_set、set、ringing、halted。在构造函数中,我们定义了状态转移,并且使用了before参数指定了两个回调函数。snnoze操作可以在两种状态下使用,因此,我们简单地将其两个状态中的前缀都设为ring。

对于闹钟来说,它最核心的功能就是算时间,因此我们需要一个时间计算器,它会每秒钟检查一遍当前时间是否达到闹铃时间,达到了就调用alarm_on()方法。我们可以这么写:

import time

alarm = Alarm()

# 设置闹钟时间
alarm.set_alarm(alarm_time=datetime.now() + timedelta(seconds=15))

while alarm.state != 'halted':
    # 发出响铃状态检查
    if alarm.state == 'ringing':
        if datetime.now() > alarm.alarm_time:
            alarm.alarm_off()
        else:
            print(str(alarm))
    # 设置闹钟时间
    elif alarm.state == 'not_set':
        input_str = input('请输入闹钟时间(格式如07:00):')
        alarm_time_str = datetime.now().strftime('%Y-%m-%d') + ' ' + input_str
        alarm_time = datetime.strptime(alarm_time_str, '%Y-%m-%d %H:%M')
        print('已设置闹钟时间{}时{}分'.format(alarm_time.hour, alarm_time.minute))
        alarm.set_alarm(alarm_time=alarm_time)
    # 其他状态
    else:
        print(str(alarm))
    time.sleep(1)

print('退出闹钟程序')

这个循环代码包含了闹钟的交互逻辑,每秒钟检查一次闹钟状态,如果闹钟处于ringing状态且闹钟时间已过了,就关闭闹钟;如果闹钟未设置,就让用户输入闹钟时间并设置;否则就输出当前状态。

示例二:多个状态机组合

有时候,我们会碰到多个业务状态机需要组合使用的问题,比如在一个工厂中,一批原材料到来后必须进行原材料检测、原材料质检、领取并清空检测台等操作,才能安排生产。这个过程可以抽象成三个状态机,它们是并列的关系。

首先我们需要定义三个状态机类:MaterialCheck、MaterialQC和MaterialClaim。这里我们只给出MaterialCheck的定义,其他状态机定义与之类似:

from transitions import Machine

class MaterialCheck(object):
    # 定义状态
    states = ['ready', 'testing', 'pass', 'fail']

    def __init__(self):
        self.machine = Machine(model=self, states=MaterialCheck.states, initial='ready')
        self.machine.add_transition('start_test', 'ready', 'testing')
        self.machine.add_transition('test_pass', 'testing', 'pass')
        self.machine.add_transition('test_fail', 'testing', 'fail')

它的状态包括ready、testing、pass和fail。在构造函数中,我们定义了三个转移事件:start_test、test_pass、test_fail,分别表示开始检测、检测通过和检测不通过三种状态。

接下来,我们可以定义主状态机组合这三个子状态机:

class MaterialAllocation(object):
    # 定义状态
    states = ['allocate', 'check', 'qc', 'production', 'complete']
    transitions = [
        {'trigger': 'start_check', 'source': 'allocate', 'dest': 'check'},
        {'trigger': 'start_qc', 'source': 'check', 'dest': 'qc', 'conditions': 'qc_ready'},
        {'trigger': 'start_production', 'source': 'qc', 'dest': 'production', 'conditions': 'production_ready'},
        {'trigger': 'complete_production', 'source': 'production', 'dest': 'complete'}
    ]

    def __init__(self, check, qc, claim):
        self.machine = Machine(model=self, states=MaterialAllocation.states, transitions=MaterialAllocation.transitions, initial='allocate')
        self.check = check
        self.qc = qc
        self.claim = claim

    # 检查是否有原材料需要检测
    def check_ready(self):
        return self.claim.count_materials > 0

    # 检查是否需要质检
    def qc_ready(self):
        return self.check.state == 'pass'

    # 检查是否已经准备好生产
    def production_ready(self):
        return self.qc.state == 'pass'

check = MaterialCheck()
qc = MaterialQC()
claim = MaterialClaim()

allocation = MaterialAllocation(check, qc, claim)

# 开始原材料的领取、检测、质检、生产流程
while allocation.state != 'complete':
    # 领取原材料
    if allocation.state == 'allocate':
        claim.count_materials -= 1
        allocation.start_check()
    # 进行原材料检测
    elif allocation.state == 'check':
        if allocation.check.state == 'ready':
            allocation.check.start_test()
        elif allocation.check.state == 'pass':
            allocation.start_qc()
        else:
            print('原材料检测不通过。')
            allocation = MaterialAllocation(check, MaterialQC(), claim)
    # 进行质量检查
    elif allocation.state == 'qc':
        if allocation.qc.state == 'ready':
            allocation.qc.start_qc()
        elif allocation.qc.state == 'pass':
            allocation.start_production()
        else:
            print('质检不通过。')
            allocation = MaterialAllocation(check, MaterialQC(), claim)
    # 进行生产
    elif allocation.state == 'production':
        allocation.complete_production()
    # 生产完成
    else:
        print('生产完成!')

这里我们定义了一个MaterialAllocation类作为主状态机,并将MaterialCheck、MaterialQC和MaterialClaim状态机作为参数传入。在主状态机构造函数中,我们定义了状态转移,并在后面的循环中使用特定的流程处理每个状态机的运行。在整个过程中,我们异步执行了三个子状态机,它们通过组合的方式实现了一个完整的业务流程。

至此,我们已经学习了如何使用transitions库来构建一个状态机,包括基本状态、状态转移和转移回调函数、业务场景的案例,相信通过这篇文章的学习和实践,大家已经对状态机有了更深入的认识。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:python状态机transitions库详解 - Python技术站

(2)
上一篇 2023年5月30日
下一篇 2023年5月30日

相关文章

  • Python3获取电脑IP、主机名、Mac地址的方法示例

    Python3获取电脑IP、主机名、Mac地址的方法示例 在Python程序中获取电脑IP、主机名和Mac地址是非常必要的操作,本文将详细讲解Python3获取电脑IP、主机名、Mac地址的方法示例。 获取电脑IP地址 获取电脑IP地址可以使用Python中的socket库中的gethostbyname函数来实现。 import socket def get…

    python 2023年5月23日
    00
  • Python语言检测模块langid和langdetect的使用实例

    下面是详细讲解“Python语言检测模块langid和langdetect的使用实例”的完整攻略。 简介 在文本分析领域,语言检测模块是一个重要的工具。Python语言提供了两种流行的语言检测模块:langid和langdetect。这两个模块都可以用来检测文本的语言,可以帮助我们进行自然语言处理、机器翻译、文本分类等任务。 langid langid是一个…

    python 2023年6月3日
    00
  • Python 列表(List) 的三种遍历方法实例 详解

    Python中的列表(List)是一种常用的数据类型,可以存储多个元素。本文将详细讲解Python中列表的三种遍历方法,包括for循环遍历、while循环遍历和列表推导式,并提供两个实例说明。 for循环遍历 使用for循环遍历列表是常见的方法。可以使用in关键字来遍历列表中的每个元素。例如: my_list = [1, 2, 3, 4, 5] for el…

    python 2023年5月13日
    00
  • python 对象和json互相转换方法

    Python 对象和 JSON 互相转换是编程中经常遇到的问题,本文将介绍 Python 中将对象转换为 JSON,以及将 JSON 转换为 Python 对象的方法。 Python 对象转换为 JSON 使用 Python 内置的 json 模块,可以将 Python 对象转换为 JSON 格式的字符串。 下面是将 Python 字典对象转换为 JSON …

    python 2023年6月3日
    00
  • 深入了解Python中字符串格式化工具f-strings的使用

    以下是深入了解Python中字符串格式化工具f-strings的使用的完整攻略: 什么是f-strings f-strings是Python3.6版本之后引入的一种字符串格式化方法,使用起来很简单,也很易读,可以在字符串中嵌入变量,从而更加便于修改和重构代码。 f-strings的使用方法 f-strings的格式为在字符串前加上字母“f”,然后使用大括号“…

    python 2023年6月5日
    00
  • python使用xslt提取网页数据的方法

    1.前言在网页数据抓取中,我们可能会遇到需要将网页中的某些结构化数据提取出来的情况,这个时候xslt语言就可以派上用场了。本文主要介绍如何使用python结合xslt语言来提取网页数据。 2.xslt语言介绍xslt是一种基于xml的语言,主要用于将xml数据文档转换成其他格式,比如html、xml、文本等。使用xslt可以强大地操作xml文档,例如选择某些…

    python 2023年6月3日
    00
  • Python装饰器的函数式编程详解

    下面我将详细讲解“Python装饰器的函数式编程详解”的完整攻略。 什么是装饰器 装饰器是Python语言中一种特殊的语法,用于装饰函数、方法或类,可以在不改变原函数/方法/类的源代码,又能在运行时动态地扩展其功能。装饰器本身是一个函数,其作用是接收一个函数/方法/类作为参数,然后返回一个新的函数/方法/类,常用于解决一些横切关注点(如日志、权限等)的问题。…

    python 2023年5月19日
    00
  • 利用Python的folium包绘制城市道路图的实现示例

    利用Python的folium包可以绘制交互式地图,包括城市道路图,以下是绘制城市道路图的详细攻略: 安装folium包: python !pip install folium 导入folium包: python import folium 获取城市道路数据: 可以从开放数据平台等公开渠道中获取城市道路数据,包括道路名称、起点经纬度、终点经纬度等信息。 示例…

    python 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部