Python 基于协程的状态机
我有一个棘手而有趣的问题要问你 在Twisted、Tornado中处理I/O任务(例如通过某个传输层实现协议)时,我发现了类似的场景或模式。这种模式是泛化的,而不是抽象的。例如,当您使用类设备时,您向他发送命令并接收结果 但是,有时您需要使用新命令对调制解调器对上一个命令的响应作出反应。例如,假设调制解调器是M,->是接受一个参数消息键的通信操作员,服务器是SPython 基于协程的状态机,python,event-handling,tornado,coroutine,fsm,Python,Event Handling,Tornado,Coroutine,Fsm,我有一个棘手而有趣的问题要问你 在Twisted、Tornado中处理I/O任务(例如通过某个传输层实现协议)时,我发现了类似的场景或模式。这种模式是泛化的,而不是抽象的。例如,当您使用类设备时,您向他发送命令并接收结果 但是,有时您需要使用新命令对调制解调器对上一个命令的响应作出反应。例如,假设调制解调器是M,->是接受一个参数消息键的通信操作员,服务器是S 1. s ->(a) M 1.1 M ->(b) S # modem reacts on `a` as
1. s ->(a) M
1.1 M ->(b) S # modem reacts on `a` as `b`; so next we should send him command B
1.2 M ->(c) S # modem responses on `a` as `c`; so next we should send him C
2. s ->(b) M
2.1 M ->(g) S
2.2 M -> (f) S
...
2.N M -> (x) S
...
因此,它看起来像FSM的行为。在tornado中实现这个场景,同时使用非阻塞I/O(通过streams对象)会很好。通过简单地提供跟踪场景作为输入,并覆盖输入中描述的状态(事件)的处理程序,我们可以实现良好的有限状态机行为
输入可能具有以下符号:
{
a: (b, c, d),
b: (c, 'exit|silence'),
c: (a, 'exit|silence'),
d: (b)
}
所有这些字母数字符号都是州名。
每个键值对都是状态名和可能的状态转换集
在tornado合作计划和期货中引入的FSM的可能实现是什么?
请分享您的想法和代码。我认为
Twisted
更适合协议实现。无论如何,在Python中,函数和方法是第一类的对象,这意味着您可以将它们存储在字典中。您还可以使用functools.partial
将带有参数的函数绑定到字典键。您可以使用它来实现转换。每个状态都应该是一个包含字典的函数,其中键是可能的输入状态,值是输出状态。然后你可以轻松地从一个州跳到另一个州。为了利用Tornado循环,应该使用ioloop.ioloop.instance().add\u callback
将下一个状态注册为回调,而不是直接调用
自动机接受语言a*b*c的示例实现:
import errno
import functools
import socket
from tornado import ioloop, iostream
class Communicator(object):
def connection_ready(self, sock, fd, events):
while True:
try:
connection, address = sock.accept()
except socket.error, e:
if e[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
raise
return
connection.setblocking(0)
self.stream = iostream.IOStream(connection)
self.stream.read_until(delimiter='\n', callback=self.initial_state)
def initial_state(self, msg):
msg = msg.rstrip()
print "entering initial state with message: %s" % msg
transitions = {
'a' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_a, msg),
'b' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_b, msg),
'c' : functools.partial(ioloop.IOLoop.instance().add_callback, self.final_state, msg)
}
try:
transitions[msg[0]]()
except:
self.stream.write("Aborted (wrong input)\n", self.stream.close)
def state_a(self, msg):
print "entering state a with message: %s" % msg
transitions = {
'a' : functools.partial(ioloop.IOLoop.instance().add_callback, self.stream.write, "got a\n", functools.partial(self.state_a, msg[1:])),
'b' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_b, msg),
'c' : functools.partial(ioloop.IOLoop.instance().add_callback, self.final_state, msg[1:])
}
try:
transitions[msg[0]]()
except:
self.stream.write("Aborted (wrong input)\n", self.stream.close)
def state_b(self, msg):
print "entering state b with message: %s" % msg
transitions = {
'a' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_a, msg),
'b' : functools.partial(ioloop.IOLoop.instance().add_callback, self.stream.write, "got b\n", functools.partial(self.state_a, msg[1:])),
'c' : functools.partial(ioloop.IOLoop.instance().add_callback, self.final_state, msg[1:])}
try:
transitions[msg[0]]()
except:
self.stream.write("Aborted (wrong input)\n" , self.stream.close)
def final_state(self, msg):
print "entering final state with message: %s" % msg
self.stream.write("Finished properly with message %s\n" % msg, self.stream.close)
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(0)
sock.bind(("", 8000))
sock.listen(5000)
communicator = Communicator()
io_loop = ioloop.IOLoop.instance()
callback = functools.partial(communicator.connection_ready, sock)
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
try:
io_loop.start()
except KeyboardInterrupt:
io_loop.stop()
print "exited cleanly"
使用Netcat的会话:
$ nc localhost 8000
aaaaa
got a
got a
got a
got a
got a
Aborted (wrong input)
$ nc localhost 8000
abababab
got a
got b
got a
got b
got a
got b
got a
got b
Aborted (wrong input)
$ nc localhost 8000
aaabbbc
got a
got a
got a
got b
got b
got b
Finished properly with message
$ nc localhost 8000
abcabc
got a
got b
Finished properly with message abc
@Veedrac现在好点了吗?