Itertools.product中的索引元素

时间:2018-12-18 14:01:12

标签: python itertools

我们可以从Python 3中的itertools.product结果获取特定索引中的元素吗?如下所示:

xlist = itertools.product('abc', repeat=3)
element = xlist[10]

更新
整个问题都颠倒了!我发现生成所有集合并寻找索引是一个大错误!我查看了问题的可能duplicate,但没有得到answer

4 个答案:

答案 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)