I'm writing a small Python application that performs model simulation for many different parameters. The iterator of parameters isn't completely known ahead of time. Rather, the iterator needs to change and adapt based on the results of the previous simulations.
The code illustrates how I implemented a prototype of the parameter scanning algorithm, but the solution feels non-pythonic (ugly, implicit, complex,....). Specifically, it feels problematic to modify the iterator over which I'm looping inside the for loop. I've consulted google looking for pythonic constructs that meet my needs, but have been unable to find any ("adaptable iterator", "modifiable generator",....).
Is there a more pythonic way to express the logic in the function scan()
?
import random as r
def scan(simulate, iterator, process):
"""A simple scanning procedure of parameters in an iterator. Perform a simulation on
each parameter in the iterator, process the output of the simulation, update the iterator"""
for parameter in iterator:
print(parameter) # for prototype logging
# simulate a model for a particular parameter value
result = simulate(parameter)
# update the process object via the call method
process.call(result)
# update the iterator based on the currently processed simulations (e.g. stochastic optimization)
iterator.update(process)
return process.finish()
# toy simulator
def mysimulate(p):
return r.random() > .5
# example adaptable iterator
class MyIter:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
def update(self, process):
"""update takes in a process object and modifies the iterator according to some algorithm to change
the values returned by __next__"""
if r.random() < .4:
self.i -= 1
class MyProcess:
"""Toy processing object."""
log = []
def call(self, trajectory):
self.log.append(trajectory)
def finish(self):
return sum(self.log)
out = scan(mysimulate, MyIter(20), MyProcess())
print(out)
def random_repeat(x): for val in itertools.repeat(x): yield val; if r.random() >= 4: break
I could propose to shorten it toprint(sum(r.random() > .5 for x in range(20) for v in random_repeat(x))
(even though you'd lose your "logging"print(v)
). But since your code is hypothetical and stripped off of all its context, this has probably nothing in common with the problem you are trying to solve. See also this meta post – Mathias Ettinger Apr 24 '16 at 14:41