参考博客
https://blog.csdn.net/mieleizhi0522/article/details/82142856
https://zhuanlan.zhihu.com/p/31526894
首先,要了解python中的几个语法
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))
1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)
2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环
3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,
4.程序执行print("*"20),输出20个
5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,
6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))
5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量
6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环
7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。
def foo(num):
print("starting...")
while num<10:
num=num+1
yield num
for n in foo(0):
print(n)
for n in range(1000):
a=n
range需要一次性生成,很占内存,使用yield就可以
然后1. 拿for i in x举例, for循环首先要求in后面对象x是一个迭代器(内部实现了__next__方法的对象) 2. 如果是可迭代对象(内部仅实现了__iter__方法)而不是迭代器的话,如果是可迭代对象则使用iter方法将其变为迭代器(加入next方法)这些都是for语句这行内部完成的操作 3. 下一步就是调用迭代器中的next方法,将返回值存入变量i中,进入循环体继续执行后面的代码了
有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。
file = open("/tmp/foo.txt")
try:
data = file.read()
finally:
file.close()
with open("/tmp/foo.txt") as file:
data = file.read()
SimPy 是离散事件驱动的仿真库。所有活动部件,例如车辆、顾客,、即便是信息,都可以用 process (进程) 来模拟。这些 process 存放在 environment (环境) 。所有 process 之间,以及与environment 之间的互动,通过 event (事件) 来进行.
process 表达为 generators (生成器), 构建event(事件)并通过 yield 语句抛出事件。
当一个进程抛出事件,进程会被暂停,直到事件被激活(triggered)。多个进程可以等待同一个事件。 SimPy 会按照这些进程抛出的事件激活的先后, 来恢复进程。
其实中最重要的一类事件是 Timeout, 这类事件允许一段时间后再被激活, 用来表达一个进程休眠或者保持当前的状态持续指定的一段时间。这类事件通过 Environment.timeout来调用。
Environment 决定仿真的起点/终点, 管理仿真元素之间的关联, 主要 API 有
simpy.Environment.process - 添加仿真进程
simpy.Environment.event - 创建事件
simpy.Environment.timeout - 提供延时(timeout)事件
simpy.Environment.until - 仿真结束的条件(时间或事件)
simpy.Environment.run - 仿真启动
from random import seed, randint
seed(23)
import simpy
# 定义一个汽车进程
def car(env):
while True:
print('Start parking at %d' % env.now)
parking_duration = 5
yield env.timeout(parking_duration) # 进程延时 5s
print('Start driving at %d' % env.now)
trip_duration = 2
yield env.timeout(trip_duration) # 延时 2s
# 仿真启动
env = simpy.Environment() # 实例化环境
env.process(car(env)) # 添加汽车进程
env.run(until=15) # 设定仿真结束条件, 这里是 15s 后停止
class EV:
def __init__(self, env):
self.env = env
self.drive_proc = env.process(self.drive(env))
self.bat_ctrl_proc = env.process(self.bat_ctrl(env))
self.bat_ctrl_reactivate = env.event()
self.bat_ctrl_sleep = env.event()
def drive(self, env):
"""驾驶进程"""
while True:
# 驾驶 20-40 分钟
print("开始驾驶 时间: ", env.now)
yield env.timeout(randint(20, 40))
print("停止驾驶 时间: ", env.now)
# 停车 1-6 小时
print("开始停车 时间: ", env.now)
self.bat_ctrl_reactivate.succeed() # 激活充电事件
self.bat_ctrl_reactivate = env.event()
yield env.timeout(randint(60, 360)) & self.bat_ctrl_sleep # 停车时间和充电程序同时都满足
print("结束停车 时间:", env.now)
def bat_ctrl(self, env):
"""电池充电进程"""
while True:
print("充电程序休眠 时间:", env.now)
#当一个进程抛出事件,进程会被暂停,直到事件被激活
yield self.bat_ctrl_reactivate # 休眠直到充电事件被激活
print("充电程序激活 时间:", env.now)
yield env.timeout(randint(30, 90))
print("充电程序结束 时间:", env.now)
self.bat_ctrl_sleep.succeed()
self.bat_ctrl_sleep = env.event()
def main():
env = simpy.Environment()
ev = EV(env)
env.run(until=300)
if __name__ == '__main__':
main()
驾驶进程,充电进程同时启动,其中要注意的就事件的使用
yield self.bat_ctrl_reactivate 抛出事件
self.bat_ctrl_reactivate.succeed() 激活事件
Resource/Store 也是另外一类重要的核心概念, 但凡仿真中涉及的人力资源以及工艺上的物料消耗都会抽象用 Resource 来表达, 主要的 method 是 request. Store 处理各种优先级的队列问题, 表现跟 queue 一致, 通过 method get / put 存放 item