在Python中记住过去(模型状态)的五种方法 从封闭函数和迭代器到状态机Python库

有人说...

"那些不能记住过去的人,注定要重复它"。G. Santayana, 1905

就像在现实生活中一样,在软件中,重要的是要知道 "状态 "是如何保存的,这可能是理想的行为或不是。状态是一个动态系统在某一时期的行为。

在软件中,系统的状态是通过保留一组相关变量的值来建模的。在一个更复杂的形式中,软件中的状态是通过状态机来建模的,其特征是状态、过渡,通常还有从一个状态过渡到另一个状态的概率。机器学习中的有限状态机(简称状态机)的例子是马尔科夫链和马尔科夫模型[1]。状态机在各种应用中都很重要。

安全关键型系统。例如,医疗设备[2]、配电系统[3]和运输系统,如铁路[4]、飞机[5]和自动驾驶汽车[6]。

区块链数据传输、存储和检索系统。区块链也被称为无限状态机[7]。

视频游戏[8],对话式人工智能[9],图形用户界面的实现[10]。

用BLAST(Basic Local Alignment Search Tool)寻找蛋白质序列的相似性[11]。

用于时间序列预测的深度状态空间模型[12]。

每个软件开发者都知道,要想在不同的函数调用之间保留一个变量的状态,最简单(也是最丑陋)的方法就是将这个变量声明为一个全局变量。但是,让我们忘掉丑陋,在管理一个虚构的、风景如画的地中海酒店的客人的用例中,描述五种不同的方法来模拟 Python 的状态。我们的酒店叫做Artemis Cypress(Artemis是古希腊的女神,而柏树是她的圣树)。

在创建管理酒店客人的功能时,我们将研究以下在 Python 中管理状态的方法。

空列表作为默认的函数参数。

Python闭包

迭代器

类变量

状态机Python库

1、默认函数参数的空列表

在我们讨论第一个用例之前,让我们注意以下几点。一般来说,将空列表作为默认函数参数是个坏主意。我们将在下面的用例中解释原因。

def roomsWithPet(roomNumber,lr=[]):    lr.append(roomNumber)    print(f"The rooms with pets are:  {lr}")    return lr

L=roomsWithPet(300)L=roomsWithPet(101)L=roomsWithPet(200)

在我们的第一个用例中,酒店的预订专家想跟踪有宠物的客人预订的房间,以便分配额外的清洁人员。因此,每当有客人预订了带宠物的房间时,她的软件就会调用下面的函数 roomsWithPet()

该函数需要一个位置参数(roomNumber)和一个命名参数lr,其默认值为一个空列表。这个函数被调用了三次,只有roomNumber这个参数。

lr发生了什么? 在函数第一次被调用时,lr被创建并填充了roomNumber的值,随后的每一次,新的roomNumber值都被附加到第一次调用时创建的lr列表上。

因此,输出是:

The rooms with pets are:  [300]The rooms with pets are:  [300, 101]The rooms with pets are:  [300, 101, 200]

在我们的例子中,这是理想的行为,但在许多情况下,你想在每次函数调用时都创建一个新的列表,这就不是了。因此,重要的是要知道,把一个空列表作为默认参数传递给一个函数,在函数调用之间保留列表的状态。

2.Python 闭包

在我们的第二个用例中,四个在酒店参加会议的客人来到前台办理退房手续。这些客人是同一家公司的员工,在酒店共用一个大套房。接待员需要计算每个人的应付余额。她可以调用下面的函数,输入的参数是套房号(501)、入住天数(3)、套房价格/天(400美元),以及每个人的额外费用(在酒店餐厅用餐)。但是调用一个函数四次,每次只有最后一个参数不同,这有点麻烦。

def checkOut(roomN,rate,nights,extra):    amountOwned=rate*nights+extra    return amountOwned

Python闭包为我们提供了一种更优雅的方式来做到这一点。它们是内部函数,可以访问外部函数中的值,甚至在外部函数执行完毕之后[13]。换句话说,闭包函数有能力记住外层函数的值,即使这些值已经不在内存中了[14]。总的来说,当我们不想写一个类时,Python闭包提供了一个很好的方法来进行数据隐藏,这是实现数据隐藏最常见的方法。

def balanceOwned(roomN,rate,nights):    def increaseByMeals(extra):        amountOwned=rate*nights+extra        print(f"Dear Guest of Room {roomN}, you have",         "a due balance:", "${:.2f}".format(amountOwned))        return amountOwned    return increaseByMeals

ba = balanceOwned(201,400,3)ba(200)ba(150)ba(180)ba(190)

在下面的代码片段中,内部函数 increaseByMeals() 是一个闭包函数,因为它记住了外部函数 balanceOwned() 的值,即使在后者执行之后。因此,这使得我们可以写出以下代码,其中balanceOwned()只被调用一次,并在其执行后,我们用每个客人的餐费调用它四次。

更加优雅了。现在的输出是。

Dear Guest of Room 201, you have a due balance: $1400.00Dear Guest of Room 201, you have a due balance: $1350.00Dear Guest of Room 201, you have a due balance: $1380.00Dear Guest of Room 201, you have a due balance: $1390.00

3.迭代器

在我们的第三个用例中,酒店的客人可以预约瑜伽教练或体育教练的个人课程。在下面的代码中,D是一个字典,键是房间号,值是 "Y "或 "PT"("Y "表示要求的瑜伽教练,"PT "表示要求的身体教练)。

D = {"123":"Y","111":"PT","313":"Y","112":"Y","201":"PT"}ff = filter(lambda e:e[1]=="Y", D.items())print(next(ff))print(next(ff))

每天早上,一个新的字典被创建,其中房间号是根据请求的时间输入的。然后,为了得到请求瑜伽的房间,执行随后的 filter() 命令。现在中央服务器已经为瑜伽教练准备好了。

每次瑜伽教练要求去下一个房间时,服务器只需调用 next(ff),就会返回下一个排队等候瑜伽教练的房间。这样一来,瑜伽教练就不需要就下一个房间的去向相互沟通。一个简单的迭代器保留了房间如何被服务的整体状态,而next()命令将我们带到下一个需要被服务的房间。

输出结果是。

('123', 'Y')('313', 'Y')

4.类变量和状态机Python库

在本节中,我们将讨论上述在Python中保留状态的最后两种方法。

类变量。这些是在类的结构中定义的变量,在类被构造时声明,并由类的所有实例共享。如果一个类的实例改变了一个类的变量,那么该类的所有实例都会受到影响,通过这种方式,类的变量在类的实例之间保留它们的状态。

Python 状态机库,是一个开源的库[15],允许定义状态和从状态到状态的转换动作。

我们将在最后一个用例中展示类变量和Python状态机库的使用,这个用例为我们酒店的桑拿房的使用提供了模型。酒店在两个不同的地方有两个桑拿房,每个都可以容纳两个人。

class Sauna(StateMachine):    #Class variable that counts the number of class instances    inst_counter=0    def __init__(self):       super().__init__()       self.entryTimes=[]       self.guestcount=0       self.__roomnumbers=[]       Sauna.inst_counter+=1

    #States    empty = State('Empty', initial=True)    space_avail = State('OneSpaceOccupied')    full = State('NoSpace')

    #Transitions    occupied = empty.to(space_avail)    guestleft = space_avail.to(empty)    nospace = space_avail.to(full)    spacefortwo = full.to(empty)    nomorefull = full.to(space_avail)

    #Actions on Transitions    def on_enter_empty(self):        print("Welcome to the Sauna Room!")

    def on_enter_space_avail(self):        print("One sauna seat available!")        currentT = DT.now()        self.__roomnumbers.append(random.randint(1,30))        self.entryTimes.append(currentT.strftime("%Y-%m-%d %H:%M:%S"))        self.guestcount+=1

    def on_enter_full(self):        print("Sorry, the Sauna Room is full!")        self.__roomnumbers.append(random.randint(1, 30))        currentT = DT.now()        self.entryTimes.append(currentT.strftime("%Y-%m-%d %H:%M:%S"))        self.guestcount+=1

    def on_enter_full(self):        print("Sorry, the Sauna Room is full!")        self.__roomnumbers.append(random.randint(1, 30))        currentT = DT.now()        self.entryTimes.append(currentT.strftime("%Y-%m-%d %H:%M:%S"))        self.guestcount+=1

    def getEntryTimes(self):        return self.entryTimes

    def getGuestCount(self):        return self.guestcount

    @classmethod    def getSaunaCount(cls):        return Sauna.inst_counter

    @property    def getroomnumbers(self):        return self.__roomnumbers

    cycle= occupied | nospace | spacefortwo

我们下面的桑拿房类继承自状态机,并有一个类变量inst_counter,用于计算该类的实例数量,每次调用该类的构造函数时都会递增。我们为每个桑拿房定义了三种状态:空(0个客人)、空闲(一个客人)和满(两个客人)。然后,我们给可能的过渡起名字。例如,nospace意味着我们已经从space_avail状态过渡到full状态。

在定义了过渡之后,我们定义了过渡的动作。例如,函数on_enter_space_avail()实现了当一个客人进入先前空的桑拿房时的动作。除了打印出有一个桑拿房座位可用外,该函数还增加实例变量guestcount。工作人员想知道每个房间有多少客人使用过,以评估特定桑拿房位置的受欢迎程度。

获取当前时间。然后将其追加到列表entryTimes中,这是一个实例变量。知道进入的时间是很有用的,这样在繁忙的时候,工作人员可以安排额外的毛巾等。

记录正在使用该房间的客人的房间号,将其追加到列表roomnumbers中。这是为了计费的目的(超过两个疗程/周,这是免费的,客人要为额外的疗程计费)。

在我们的案例中,房间号是随机生成的。另外,值得注意的是,roomnumbers列表是类的一个私有成员,为了访问它,我们定义了getroomnumbers这个属性。

最后,我们定义了循环,它是一个状态的管道(序列):已被占用、无空间、空间二。

下面我们创建一个桑拿房的实例,并通过调用cycle()进行状态转换。

ArtemisSauna1 = Sauna()ArtemisSauna1.cycle()ArtemisSauna1.cycle()ArtemisSauna1.cycle()

输出显示如下。在第一次调用cycle()后,它处于被占用的状态,所以它打印出 "有一个桑拿座位"。在第二次调用后,它处于nospace状态,并打印出 "对不起,桑拿房已满"。在第三次调用后,它处于spacefortwo状态,并打印出 "欢迎来到桑拿房!"。

One sauna seat available!Sorry, the Sauna Room is full!Welcome to the Sauna Room!

现在,让我们得到一些额外的信息:客人的进入时间、当前状态、到目前为止的客人数量和房间号。

L=ArtemisSauna1.getEntryTimes()print(L)print(ArtemisSauna1.current_state)c=print(f"The number of guests is: {ArtemisSauna1.getGuestCount()}")ic=print(f"The number of used saunas is: {ArtemisSauna1.getSaunaCount()}")L3=ArtemisSauna1.getroomnumbersprint(L3)

下面是上述代码的输出。正如预期的那样,我们目前处于空状态,到目前为止有两个客人使用了ArtemisSauna1,而且我们目前有一个Sauna类的实例。

['2022-06-10 14:46:35', '2022-06-10 14:46:35']State('Empty', identifier='empty', value='empty', initial=True)The number of guests is: 2The number of used saunas is: 1[28, 26]

Python 提供了许多保留状态的方法,或者换句话说,保持对进程的跟踪。这很有价值,特别是对于安全关键系统,软件同时跟踪许多物理过程的状态,并将状态变化映射到适当的行动。

另一方面,正如许多GUI(图形用户界面)开发者所证实的那样,最恼人的缺陷之一是GUI不响应用户的行动,而是被锁定在一个特定的状态。总之,无论我们的目标是否是保留状态,我们都需要注意Python的保留状态的特异性。

本文由 mdnice 多平台发布

Python中记住过去(模型状态)的五种方法相关推荐

  1. Python实现将内容写入文件的五种方法总结

    本篇带你详细看一下python将内容写入文件的方法以及细节,主要包括write()方法.writelines() 方法.print() 函数.使用 csv 模块.使用 json 模块,需要的可以参考一 ...

  2. python中字符串转成数字的几种方法

    在python列表操作中,面对需要把列表中的字符串转为礼拜的操作,无需强转,通过简单的几步就可以实现,本文介绍python中字符串转成数字的三种方法:1.使用join的方法:2.使用int函数将16进 ...

  3. python中for循环遍历列表的几种方法

    列表在使用过程中,经常需要遍历列表的所有元素,对每个元素执行相同的操作.今天介绍python中for循环遍历列表的几种方法. 方法1:使用for循环简单结构遍历 首先我们新建一个城市列表,然后分别展示 ...

  4. 在Python中查找子字符串索引的5种方法

    在Python中查找字符串中子字符串索引的5种方法 (5 Ways to Find the Index of a Substring in Strings in Python) str.find() ...

  5. python中实现上下文管理器的两种方法

    上下文管理器: python中实现了__enter__和__exit__方法的对象就可以称之为上下文管理器 实现方法一举例: def File(object): def __init__(self, ...

  6. python中计算n次方运算的四种方法【转】

    https://blog.csdn.net/u011699626/article/details/119582754 这里介绍一下python中n次方运算的四种书写形式,代码如下: # -*- cod ...

  7. python中list列表删除元素的4种方法

    在python列表中删除元素主要分为以下3种场景: 根据目标元素所在的索引位置进行删除,可以使用del关键字或pop()方法: 根据元素本身的值进行删除,可使用列表(list类型)提供的remove( ...

  8. Java中double类型大小比较的五种方法

    文章目录 1.使用BigDecimal 2.使用包装类Double 3.在误差范围内运行相等 4.转换成字符串 5.使用doubleToLongBits()方法 在Java中 int类型数据的大小比较 ...

  9. 驱动中获取PsActiveProcessHead变量地址的五种方法

    PsActiveProcessHead的定义: 在windows系统中,所有的活动进程都是连在一起的,构成一个双链表,表头是全局变量PsActiveProcessHead,当一个进程被创建时,其Act ...

  10. 网页中使用CSS样式表的五种方法

    一.使用STYLE属性 将STYLE属性直接加在个别的元件标签里,<元件(标签) STYLE="性质(属性)1: 设定值1; 性质(属性)2: 设定值2; ...} 例如:  < ...

最新文章

  1. 5如何将表格的一行数据清空_微信公众号推文中如何自定义添加表格?
  2. 轻松掌握mysql数据库锁机制的相关原理_轻松掌握MySQL数据库锁机制的相关原理...
  3. 逐行阅读redux源码(二)combineReducers
  4. Angular中提示:Can't bind to 'ngModel' since it isn't a known property of 'input'
  5. 第七章:暴力求解法。第二部分
  6. 统计学习方法 李航---第7章 支持向量机
  7. elasticsearch 条件去重_elasticsearch 笔记四 之聚合查询之去重计数、基础统计、百分位、字符串统计...
  8. linux中引入python的tkinter模块
  9. 远程调用——hessian使用入门
  10. 反转单链表的几种方法
  11. 回声状态网络(echo state network,ESN)概述
  12. stm32f107基本资料
  13. 《以太坊攻略》,小白如何逆袭成为技术大咖?要学的全在这里了
  14. POJ1149 PIGS 题解
  15. 玄学小问题之input.focus()没生效的问题
  16. matlab让一个点变为指定颜色,MATLAB中的颜色控制
  17. 高二会考计算机操作题试题及答案,2017高二数学会考试题及答案_高二会考答案(数学)(5)...
  18. openNI驱动控制kinect马达
  19. 机器视觉在电子元器件中的全方位识别检测
  20. kubernetes各组件介绍

热门文章

  1. VisionPro与C# 界面显示视觉结果图像
  2. 前端技术(2)— CSS(超详解)
  3. IOTServiceSetup工具下载安装
  4. 【调剂】江苏科技大学2023年硕士研究生招生拟调剂公告
  5. 网站改版怎样降低百度惩罚及快速恢复排名?
  6. 80211 BA/BAR
  7. intitle,inurl,filetype,site的作用以及在google中的用法
  8. php利用socket_pair进程通信,nginx 进程间通信-socketpair
  9. 计算机单招知识点重点,(完整版)2018年单招考试《数学》必背知识点(一),推荐文档.docx...
  10. 自然资源部第三地理信息制图院与Bigemap强强联手,共同推动多元化新GIS应用