我们可以从Python 3中的itertools.product结果获取特定索引中的元素吗?如下所示:
xlist = itertools.product('abc', repeat=3)
element = xlist[10]
更新
整个问题都颠倒了!我发现生成所有集合并寻找索引是一个大错误!我查看了问题的可能duplicate,但没有得到answer!
答案 0 :(得分:2)
假设您使用的是Python 3,则可以使用enumerate来过滤生成器的索引,然后使用next:
import itertools
it = enumerate(itertools.product('abc', repeat=3))
result = next(e for i, e in it if i == 10)
print(result)
输出
('b', 'a', 'b')
答案 1 :(得分:2)
itertools.product()
的结果不是列表,而是可迭代的-这就是为什么它不起作用,您无法按索引访问可迭代的原因。
您可以使用list()
进行列表-但这意味着计算 all 值,这可能会非常低效。在示例情况下,虽然我们必须计算出导致所需值的所有值,但我们不需要将它们全部存储在内存中,也不需要在获得所需值后计算其余值。 / p>
如果只需要一个元素,更好的解决方案是只消耗所需的零件-这可以通过itertools.islice()
轻松完成。
element = next(itertools.islice(xlist, 10, None))
由于切片是另一个可迭代的切片,因此我们使用next()
获取第一项。
islice
的功能与列表切片非常相似(顾名思义)。请注意,一旦您消耗掉了一些可迭代的对象,则从您中断的地方开始进一步处理它。您应该在一次迭代中获得所需的东西,或者创建一个列表(或其他适当的数据结构)。
islice
与其他不使用您不感兴趣的初始值的方式相比,最大的优势在于它在Python中非常有效地实现,因此可能是最快的选择,而且如果以后只需要一个以上的元素,则可以灵活选择。
答案 2 :(得分:2)
在到达索引之前,不必迭代生成器,但是可以按照here中的说明在O(1)中立即生成该乘积。使其适应(lst, repeat)
格式:
def nth_product(lst, repeat, n):
k = len(lst)
return [lst[n // (k**r) % k] for r in range(repeat-1, -1, -1)]
示例:
>>> lst = list(range(10))
>>> ref = list(itertools.product(lst, repeat=3))
>>> all(nth_product(lst, 3, i) == list(r) for i, r in enumerate(ref))
True
答案 3 :(得分:1)
其他解决方案可以得出正确的结果,但是,如果您只想要笛卡尔积的一个元素,则迭代itertools.product
返回的生成器并不是最有效的解决方案。您可以直接构建所需的项目,而不必使用如下功能来遍历所有元素:
from collections.abc import Sequence
def product_item(idx, *seqs, repeat=None):
# Ensure inputs are actual sequences (list, tuple, str...)
seqs = [seq if isinstance(seq, Sequence) else list(seq) for seq in seqs]
# Repeat if needed
if repeat is not None:
seqs = seqs * repeat
# Compute how many items does it take to advance on each sequence
step = 1
for seq in seqs:
step *= len(seq)
# Build product item
item = [None] * len(seqs)
for i, seq in enumerate(seqs):
step //= len(seq)
seq_idx = idx // step
idx %= step
item[i] = seq[seq_idx]
return tuple(item)
print(product_item(10, 'abc', repeat=3))
# ('b', 'a', 'b')
此解决方案的复杂度为O(1)。快速比较:
import itertools
# Solution with islice
product_item_islice = lambda idx, *seqs, repeat=None: next(
itertools.islice(itertools.product(*seqs, repeat=repeat), idx, None))
idx = 100_000_000
seqs = ['abcdefgh']
repeat = 10
print(product_item(idx, *seqs, repeat=repeat))
# ('a', 'f', 'h', 'f', 'd', 'g', 'a', 'e', 'a', 'a')
print(product_item_islice(idx, *seqs, repeat=repeat))
# ('a', 'f', 'h', 'f', 'd', 'g', 'a', 'e', 'a', 'a')
%timeit product_item(idx, *seqs, repeat=repeat)
# 3.7 µs ± 46.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit product_item_islice(idx, *seqs, repeat=repeat)
# 448 ms ± 7.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)