I am using simpy to model of manufacturing equipment. The equipment need to make a product using some specified materials. I tried to model this using FilterStore as shown in the following code.
import simpy
class Material:
def __init__(self,name):
self.name = name
class Machine(object):
def __init__(self,env):
self.env = env
self.inputs = simpy.FilterStore(env)
def run(self):
mat = [ self.inputs.get(lambda i: i.name == itemname ) for itemname in ["mat1","mat2","mat4"] ]
res = yield self.env.all_of(mat)
print([res.events[i]._value.name for i in range(len(res.events))])
def input_materials(env,m):
for i in range(5):
m.inputs.put(Material( "mat"+str(i) ))
m.inputs.put(Material( "mat"+str(i) ))
m.inputs.put(Material( "mat"+str(i) ))
yield env.timeout(1)
env = simpy.Environment()
machine = Machine(env)
env.process(machine.run())
env.process(input_materials(env,machine))
env.run()
The above code is output as follows.
['mat4', 'mat4', 'mat4']
I want to get [mat1,mat2,mat4], but the above code all gets mat4. We have confirmed that the results are as expected if I do not use variable, itemname, and code it separately as follows.
def run(self):
m1 = self.inputs.get(lambda i: i.name == "mat1" )
m2 = self.inputs.get(lambda i: i.name == "mat2" )
m4 = self.inputs.get(lambda i: i.name == "mat4" )
res = yield self.env.all_of([m1,m2,m4])
print([res.events[i]._value.name for i in range(len(res.events))])
How should I code this? Some assistance would be greatly appreciated.
This is subtle.
The itemname in you lambda is the same itemname used in the for
statement
but, the lambda does not get executed till after for
statement is done,
meanning when the lambdas finally do run they are all using the same itemname variable from the for
statement with the for
statement's last for loop assignment of mat4.
This is also know as a enclosure which can be quite useful
To fix this your lambda needs a local itemname variable
update
mat = [ self.inputs.get(lambda i: i.name == itemname ) for itemname in ["mat1","mat2","mat4"] ]
to
mat = [ self.inputs.get(lambda i,itemname=itemname: i.name == itemname ) for itemname in ["mat1","mat2","mat4"] ]