状态模式和事件触发切换状态可以整合什么库来做 Python 程序设计?

2020-04-30 11:05:51 +08:00
 oahebky

一个程序针对有(一堆) object(s) 的不同状态执行不同的 actions ( functions )。

但是状态的切换是来自外部事件(非 http 请求,不过转换成 http 请求也 OK )触发。 外部事件触发可以做成 1. http 请求,2. 消息队列同步 等等

手写一个状态模式应该不算问题,设计模式的实现大同小异。

但是外部事件触发应该怎么做才好??? 就是用什么库写起来代码“很自然”。

举个例子:

比如我大学做过一个 MFC 应用,即,写的是 C++ 的桌面程序,点击按钮的事件触发 MFC 框架都生成做好了,然后只需要对按钮的 handler 函数编写实现即可。



之所以有这个问题是因为我还没有怎么写过 python 的事件触发程序(非桌面程序,非 web 程序,一个后台 daemon 程序),所以不清楚“最佳实践”是怎么样的?

2603 次点击
所在节点    Python
10 条回复
ClericPy
2020-04-30 11:09:51 +08:00
事件驱动用 pyee
oahebky
2020-04-30 11:11:30 +08:00
@ClericPy #1 原文:“事件驱动用 pyee”
======
回复:OK,我马上去了解一下。
bwangel
2020-04-30 11:21:25 +08:00
https://github.com/jek/blinker

blinker 可以用来定义事件。不过这个都是同步的。

可以再描述一下详细需求么?内部状态切换是否是耗时较长任务,如果耗时较长,需要上队列。
oahebky
2020-04-30 11:31:02 +08:00
@bwangel #3 原文:“https://github.com/jek/blinkerblinker 可以用来定义事件。不过这个都是同步的。可以再描述一下详细需求么?内部状态切换是否是耗时较长任务,如果耗时较长,需要上队列。”
======
回复:一个 object 内有不同的状态,
有的状态切换时间长(处于几小时,几天后的等待状态),有的状态切换时间短 - 计算或网络请求。

简化状态其实也就是:

状态 1 -> 状态 1 的行为 - 等待 -> 外部事件触发 => 切换到状态 2
状态 2 -> 状态 2 的行为 - 计算得到结果 => 切换到状态 1

就是切换状态不是单纯的状态模式里面的函数执行结束或者 sleep 就切换,
而是来自外部的事件(其它 python 进程对它)触发,

就是这一点不清楚选择什么样的库写起来比较不会重复造轮子。
brucedone
2020-04-30 13:05:53 +08:00
qile1
2020-05-01 00:27:34 +08:00
感觉这个有点像微信公众号 回复消息的使用
不过那个不会几个小时执行
oahebky
2020-05-01 12:42:22 +08:00
@bwangel
后来我思考了一下,对于我来说,使用哪种程序设计更像是程序是 C 端还是 S 端的问题。

如果是 S 端的话(比如电脑桌面程序相对于用户性质上也是 S 端),那么使用你推荐的 blinker 应该是很好的选择(我把它 star 了,谢谢)。

如果是 C 端的话,主要是一个请求主动发出去,等待响应。
在我的实际情况上,更像 C 端。

不过怎么做到异步是我正在研究的,因为 rabbitMQ 的 pika 和 aio-pika 我都还没好好使用过,之前只是了解到。
oahebky
2020-05-01 12:53:53 +08:00
@brucedone
这个库还在学怎么用,应该可以少写不少重复的代码。

后来想了想,对于我自己的问题,难点在于怎么做到异步。
当时提问的时候默认意思就是异步执行许多个具有状态模式设计的对象,原文没有说清楚;因为觉得解决了同步的状态模式循环,事件触发,就能加入 asyncio 迁移成异步。

但是后来发现我就是不知道怎么做到不同的触发事件(非 sleep,非 http request )怎么 yield 出去,后来想来我应该主要是要解决学会 aio-pika 怎么使用的问题。
bwangel
2020-05-04 11:29:14 +08:00
@oahebky

感觉你这个状态切换的模型没必要用库,因为状态变更的逻辑你始终都是要写的,可以直接手撸一个。

如果是 HTTP 请求触发的话,需要考虑一下并发问题,一个状态正在执行切换过程中,又有一个 HTTP 请求进来了。

所以,建议

当 HTTP 请求进来后,不立刻执行状态切换的操作,而是将它包装成一个事件,放到一个队列中,另外再起一个 Worker (可以用多个 Worker,根据你的实际情况确定,不会发生冲突即可),执行状态切换的操作。这样状态切换操作的执行时间过长,也不会有什么问题.


事件+队列可以直接使用 RabbitMQ + Celery (千万别用 Redis+Celery,不成熟,有 bug)。如果觉得这两个太重,而业务量又不是太大的话,可以用 MySQL 自己写一个队列。
oahebky
2020-05-04 11:41:45 +08:00
@bwangel
这么设计这个程序我想了几天了(自己业余为了学习做项目),重新学了学 python 异步,可以说得出的结论和你完全一致。

其实我最初是在纠结怎么在单个异步线程中,创建的以万为单位的(含有状态切换的)对象之间调度。
后来发现要用 rabbitmq 的话,相当于要创建出同数量的 queue ;
否则的话就要自己造一个基于对象 ID 的 loop 库 -- 对含有 ID 的 message 用 gener.send(msg) 激活对应的 gener 。

不论哪种方式都有些过度了。

所以还是读到带 ID 的 message 之后,重新创建对象,执行完任务销毁对象,loop 回去等待消息队列;这样设计目前来看是合理的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/667531

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX