1 分享流畅的python一书, coroutine 章节中的出租车仿真的例子.
2
3 from collections import namedtuple
4 import queue
5 import random
6 Event = namedtuple('Event', 'time process action')
7
8 def taxi_simu(ident, trips, start_t =0 ):
9 time = yield Event(start_t, ident, 'get out of garage')
10 for i in range(trips):
11 time = yield Event(time, ident, 'pick up passenger')
12 time = yield Event(time, ident, 'passenger arrive destination')
13
14 yield Event(time, ident, 'off duty, going home')
15
16
17 class Emulator(object):
18
19 def __init__(self, proc_mapping):
20 self.events = queue.PriorityQueue() # 保存排定事件的 PriorityQueue 对象,按时间正向排序。
21 self.process = dict(proc_mapping) # 获取的 procs_map 参数是一个字典(或其他映射),可是又从中构建一个字典,创建本地副本,
22 # 因为在仿真过程中,出租车回家后会从 self.procs 属性中移除,而我们不想修改用户传入的对象
23 '''
24 优先队列是离散事件仿真系统的基础构件:创建事件的顺序不定,放入这种队列之后,可以按照各个事件排定的时间顺序取出。
25 '''
26
27 def comupte_duration(self, previous_action):
28 if 'get out of garage' in previous_action:
29 return random.randint(1,3)
30 elif 'pick up passenger' in previous_action:
31 return random.randint(9,100)
32 elif 'passenger arrive destination' in previous_action:
33 return random.randint(5,10)
34 else:
35 return 8
36 def run(self, end_time):
37 for _, process in sorted(self.process.items()): # 使用 sorted 函数获取 self.procs 中按键排序的元素;用不到键,因此赋值给 _。
38 first_event = next(process) # 调用 next(proc) 预激各个协程,向前执行到第一个 yield 表达式,做好接收数据的准备。产出一个 Event 对象。
39 self.events.put(first_event) # 把各个事件添加到 self.events 属性表示的 PriorityQueue 对象中。
40
41 simu_time = 0 # 把 sim_time 变量(仿真钟)归零
42 while simu_time < end_time:
43 if self.events.empty(): # 如果队列中没有未完成的事件,退出主循环
44 print('empty events queue, all events done')
45 break
46
47 current_event = self.events.get() # 获取优先队列中 time 属性最小的 Event 对象;这是当前事件(current_event)
48 simu_time, process_id, previous_action = current_event # 拆包 Event 对象中的数据。这一行代码会更新仿真钟 sim_time,对应于事件发生时的时间。
49 # 这通常是离散事件仿真:每次循环时仿真钟不会以固定的量推进,而是根据各个事件持续的时间推进
50 print('Taxi : ', process_id, process_id * ' ', current_event) # 显示 Event 对象,指明是哪辆出租车,并根据出租车的编号缩进
51 actived_process = self.process[process_id] # 从 self.procs 字典中获取表示当前活动的出租车的协程
52 next_time = simu_time + self.comupte_duration(previous_action)
53 try:
54 next_event = actived_process.send(next_time) # 把计算得到的时间发给出租车协程。协程会产出下一个事件(next_event),或者抛出 StopIteration 异常(完成时)
55 except StopIteration:
56 del self.process[process_id] # 如果抛出了 StopIteration 异常,从 self.procs 字典中删除那个协程
57 else:
58 self.events.put(next_event) # 否则,把 next_event 放入队列中
59 else:
60 msg = '=== end of simulation time : {} events are pending ==='
61 print(msg.format(self.events.qsize())) # 如果循环由于仿真时间到了而退出,显示待完成的事件数量(有时可能碰巧是零, 如 endtime 足够大,就不有 event pending, 都会处理完)
62
63
64
65
66
67 if __name__ == '__main__':
68 taxi_num =5
69 DEPARTURE_INTERVAL = 6
70 end_time = 3333
71 taxis = {i: taxi_simu(i, (i+1)*2, i*DEPARTURE_INTERVAL)
72 for i in range(taxi_num)}
73
74 simu = Emulator(taxis)
75 simu.run(end_time)
76
77
78 '''
79 OUTPUT,
80
81 Taxi : 0 Event(time=0, process=0, action='get out of garage')
82 Taxi : 0 Event(time=3, process=0, action='pick up passenger')
83 Taxi : 1 Event(time=6, process=1, action='get out of garage')
84 Taxi : 1 Event(time=7, process=1, action='pick up passenger')
85 Taxi : 2 Event(time=12, process=2, action='get out of garage')
86 Taxi : 2 Event(time=15, process=2, action='pick up passenger')
87 Taxi : 3 Event(time=18, process=3, action='get out of garage')
88 Taxi : 3 Event(time=21, process=3, action='pick up passenger')
89 Taxi : 1 Event(time=22, process=1, action='passenger arrive destination')
90 Taxi : 4 Event(time=24, process=4, action='get out of garage')
91 Taxi : 1 Event(time=27, process=1, action='pick up passenger')
92 Taxi : 4 Event(time=27, process=4, action='pick up passenger')
93 Taxi : 3 Event(time=34, process=3, action='passenger arrive destination')
94 Taxi : 3 Event(time=40, process=3, action='pick up passenger')
95 Taxi : 0 Event(time=53, process=0, action='passenger arrive destination')
96 Taxi : 0 Event(time=63, process=0, action='pick up passenger')
97 Taxi : 3 Event(time=86, process=3, action='passenger arrive destination')
98 Taxi : 1 Event(time=94, process=1, action='passenger arrive destination')
99 Taxi : 2 Event(time=94, process=2, action='passenger arrive destination')
100 Taxi : 3 Event(time=96, process=3, action='pick up passenger')
101 Taxi : 2 Event(time=100, process=2, action='pick up passenger')
102 Taxi : 1 Event(time=101, process=1, action='pick up passenger')
103 Taxi : 4 Event(time=108, process=4, action='passenger arrive destination')
104 Taxi : 4 Event(time=116, process=4, action='pick up passenger')
105 Taxi : 3 Event(time=120, process=3, action='passenger arrive destination')
106 Taxi : 3 Event(time=130, process=3, action='pick up passenger')
107 Taxi : 0 Event(time=132, process=0, action='passenger arrive destination')
108 Taxi : 4 Event(time=132, process=4, action='passenger arrive destination')
109 Taxi : 0 Event(time=142, process=0, action='off duty, going home')
110 Taxi : 4 Event(time=142, process=4, action='pick up passenger')
111 Taxi : 3 Event(time=172, process=3, action='passenger arrive destination')
112 Taxi : 3 Event(time=180, process=3, action='pick up passenger')
113 Taxi : 2 Event(time=187, process=2, action='passenger arrive destination')
114 Taxi : 1 Event(time=192, process=1, action='passenger arrive destination')
115 Taxi : 4 Event(time=192, process=4, action='passenger arrive destination')
116 Taxi : 1 Event(time=197, process=1, action='pick up passenger')
117 Taxi : 2 Event(time=197, process=2, action='pick up passenger')
118 Taxi : 4 Event(time=198, process=4, action='pick up passenger')
119 Taxi : 3 Event(time=215, process=3, action='passenger arrive destination')
120 Taxi : 3 Event(time=223, process=3, action='pick up passenger')
121 Taxi : 1 Event(time=231, process=1, action='passenger arrive destination')
122 Taxi : 4 Event(time=236, process=4, action='passenger arrive destination')
123 Taxi : 1 Event(time=238, process=1, action='off duty, going home')
124 Taxi : 4 Event(time=241, process=4, action='pick up passenger')
125 Taxi : 2 Event(time=297, process=2, action='passenger arrive destination')
126 Taxi : 2 Event(time=305, process=2, action='pick up passenger')
127 Taxi : 3 Event(time=322, process=3, action='passenger arrive destination')
128 Taxi : 3 Event(time=329, process=3, action='pick up passenger')
129 Taxi : 4 Event(time=338, process=4, action='passenger arrive destination')
130 Taxi : 4 Event(time=344, process=4, action='pick up passenger')
131 Taxi : 2 Event(time=348, process=2, action='passenger arrive destination')
132 Taxi : 2 Event(time=353, process=2, action='pick up passenger')
133 Taxi : 3 Event(time=357, process=3, action='passenger arrive destination')
134 Taxi : 3 Event(time=366, process=3, action='pick up passenger')
135 Taxi : 4 Event(time=425, process=4, action='passenger arrive destination')
136 Taxi : 4 Event(time=431, process=4, action='pick up passenger')
137 Taxi : 3 Event(time=436, process=3, action='passenger arrive destination')
138 Taxi : 3 Event(time=442, process=3, action='off duty, going home')
139 Taxi : 2 Event(time=446, process=2, action='passenger arrive destination')
140 Taxi : 2 Event(time=452, process=2, action='pick up passenger')
141 Taxi : 4 Event(time=520, process=4, action='passenger arrive destination')
142 Taxi : 2 Event(time=523, process=2, action='passenger arrive destination')
143 Taxi : 4 Event(time=525, process=4, action='pick up passenger')
144 Taxi : 2 Event(time=531, process=2, action='off duty, going home')
145 Taxi : 4 Event(time=622, process=4, action='passenger arrive destination')
146 Taxi : 4 Event(time=632, process=4, action='pick up passenger')
147 Taxi : 4 Event(time=642, process=4, action='passenger arrive destination')
148 Taxi : 4 Event(time=648, process=4, action='pick up passenger')
149 Taxi : 4 Event(time=661, process=4, action='passenger arrive destination')
150 Taxi : 4 Event(time=667, process=4, action='off duty, going home')
151 empty events queue, all events done
152
153 '''