基本上我正在寻找itertools.product
的实现,它允许我更改生成组合的顺序。
示例:如果我使用itertools.product('AB', 'xy')
,则会按照以下确切顺序生成组合:
[('A', 'x'), ('A', 'y'), ('B', 'x'), ('B', 'y')]
我需要一个响应请求的实现,例如"请将A更改为B next",例如:
>>> generator = DynamicOrderProduct({'var1': 'AB', 'var2': 'xy'})
>>> generator.first()
{'var1': 'A', 'var2': 'x'}
>>> generator.change('var1')
{'var1': 'B', 'var2': 'x'}
>>> generator.change('var2')
{'var1': 'B', 'var2':, 'y'}
>>> generator.change('var2') # here it can't generate a new combination by
# changing var2, so it changes var1 instead
{'var1': 'A', 'var2': 'y'}
>>> generator.change('var2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
理想情况下,生成器会接受这样的变量列表:
generator.change(['var1', 'var2'])
然后应该尝试更改var1
的值,如果不可能,请改为更改var2
的值,依此类推。
我将如何实施此功能?标准库中有什么东西可以帮助我吗?
答案 0 :(得分:0)
好吧,我已经成功编写了一个可以实现我想要的迭代器。这是我写过的最丑陋的代码,但它完成了工作。
我仍然希望有一个更好的解决方案 - 这个实现保留了一组所有返回的组合,这些组合可以增长到使用相当多的内存。
class DynamicOrderProduct:
"""
Given a dict of {variable: [value1,value2,value3,...]}, allows iterating
over the cartesian product of all variable values.
Each step in the iteration returns a mapping of {variable: value}.
To start the iteration, retrieve the first mapping by calling .first().
To retrieve subsequent mappings, call
.next(order_in_which_to_change_variable_values). This function's
parameter should be a list of variables sorted by which variable's value
should change next. If possible, the first variable in the list will
change value. If not, the 2nd variable in the list will change value
instead, and so on. Raises StopIteration if all combinations are
exhausted.
Example:
possible_values = {'B': [0,1], # B can take the value 0 or the value 1
'L': [1,2,3]}
iterator = DynamicOrderProduct(possible_values)
print(iterator.first())
import random
variables = list(possible_values.keys())
while True:
order = random.sample(variables, len(variables))
print('advancing variables in this order:', order)
try:
print(iterator.next(order))
except StopIteration:
break
You may also pass an incomplete list of variables to the .next function.
If no new combination of the given variables is possible, StopIteration is
raised. For example:
iterator = DynamicOrderProduct({var1: [1],
var2: [1,2]})
iterator.first() # this returns {var1: 1, var2: 1}
iterator.next([var1]) # raises StopIteration
Also, you may pass multiple lists to .next in order to change the value of
multiple variables. StopIteration will be raised only if no variable can
change value.
iterator = DynamicOrderProduct({var1: [1,2],
var2: [1,2]})
iterator.first() # this returns {var1: 1, var2: 1}
iterator.next([var1], [var2]) # returns {var1: 2, var2: 2}
"""
def __init__(self, possible_variable_values):
self.possible_variable_values = {k:tuple(v) for k,v in \
possible_variable_values.items()}
self.variable_order = list(possible_variable_values)
self.exhausted_combinations = set()
def first(self):
self.mapping = {var:vals[0] for var,vals in \
self.possible_variable_values.items()}
t = tuple(self.mapping[var] for var in self.variable_order)
self.exhausted_combinations.add(t)
return self.mapping
def next(self, *orders):
def advance(order, index, maxindex=2147483648):
while True: # loop to reduce recursion
try:
variable = order[index]
except IndexError:
raise StopIteration
value = self.mapping[variable]
valindex = self.possible_variable_values[variable].index(value)
start_index = valindex
while True: # change the value until we find a new combination
valindex += 1
try:
possible_values = self.possible_variable_values
value = possible_values[variable][valindex]
except IndexError:
valindex = 0
value = self.possible_variable_values[variable][0]
self.mapping[variable] = value
# if we've tried all values but none of them
# worked, try to change the next variable's
# value instead
if valindex == start_index:
if index+1 >= maxindex:
raise StopIteration
# instead of recursing, update our own parameters and
# start a new iteration
index += 1
break
t = tuple(self.mapping[var] for var in self.variable_order)
# if this combination isn't new, try
# changing the previous variables' values
if t in self.exhausted_combinations:
if index == 0:
continue
try:
return advance(order, 0, index)
except StopIteration:
continue
return t
total_order = []
fail = True
for order in orders:
# each iteration may also change the previous
# iterations' variables
total_order = order + total_order
try:
t = advance(total_order, 0)
except StopIteration:
fail = True
else:
fail = False
if fail:
raise StopIteration
self.exhausted_combinations.add(t)
return self.mapping