itertools和strided list assignment

时间:2011-12-01 04:30:23

标签: python slice itertools

给出一个列表,例如x = [True]*20,我想将False分配给其他所有元素。

x[::2] = False

提出TypeError: must assign iterable to extended slice

所以我天真地认为你可以这样做:

x[::2] = itertools.repeat(False)

x[::2] = itertools.cycle([False])

然而,据我所知,这会导致无限循环。为什么会出现无限循环?是否有一种替代方法不涉及在分配之前知道切片中元素的数量?

编辑:我理解x[::2] = [False] * len(x)/2在这种情况下有效,或者你可以在更一般的情况下为右边的乘数提出一个表达式。我试图理解是什么导致itertools无限循环,以及为什么列表赋值的行为与numpy数组赋值不同。我认为python必须有一些基本的东西,我误解了。我原本也在考虑可能有性能原因,更喜欢使用itertools来列出理解或创建另一个n元素列表。

5 个答案:

答案 0 :(得分:3)

您在此代码中尝试做的不是您的想法(我怀疑) 例如:
x[::2]将返回包含x的每个odd元素的切片,因为x大小为20,
切片的大小为10,但是您尝试为其分配不可迭代的大小为1。

成功使用您需要执行的代码:

x = [True]*20
x[::2] = [False]*10

将为大小为10的切片指定大小为10的可迭代。

为什么在黑暗中使用元素数量?使用

len(x[::2])  

等于10,然后使用

x[::2] = [False]*len(x[::2])

您还可以执行以下操作:

x = [True if (index & 0x1 == 0) else False for index, element in enumerate(x)]

编辑:由于OP编辑

循环documentation表示它Repeats indefinitely.,这意味着它将通过已经给出的迭代器连续“循环”。

重复有一个类似的实现,但是documentation声明它 Runs indefinitely unless the times argument is specified.
这在问题代码中没有完成。因此,两者都将导致无限循环。

关于itertools更快的评论。是的,itertools通常比其他实现更快,因为它们被优化为与创建者一样快。

但是,如果您不想重新创建列表,可以使用generator expressions,如下所示:

x = (True if (index & 0x1 == 0) else False for index, element in enumerate(x))

它不会将所有元素存储在内存中,而是在需要时生成它们,但是,生成器函数可以用完。

例如:

x = [True]*20
print(x)
y = (True if (index & 0x1 == 0) else False for index, element in enumerate(x))
print ([a for a in y])
print ([a for a in y])

将打印x然后打印生成器y中的元素,然后打印一个空列表,因为生成器已用完。

答案 1 :(得分:2)

正如Mark Tolonen在一篇简明的评论中指出的那样,你的itertools尝试无限循环的原因是因为,对于列表赋值,python正在检查右边的长度。

现在真正深入挖掘...

当你说:

x[::2] = itertools.repeat(False)

左侧(x[::2])是一个列表,您正在为值为itertools.repeat(False) iterable的列表分配一个值,该值将永远迭代,因为它没有给出长度(根据the docs)。

如果你深入研究cPython实现中的列表赋值代码,你会发现不幸/痛苦命名的函数list_ass_slice,它是很多列表赋值的根源。在该代码中,您会看到this segment

v_as_SF = PySequence_Fast(v, "can only assign an iterable");
if(v_as_SF == NULL)
    goto Error;
n = PySequence_Fast_GET_SIZE(v_as_SF);

这里尝试获取您分配给列表的可迭代的长度(n)。然而,在它到达那里之前它会被卡在PySequence_Fast上,它最终试图将你的iterable转换为一个列表(带有PySequence_List),在这个列表中它最终创建一个空列表并尝试简单用你的iterable扩展它。

要使用iterable扩展列表,它使用listextend(),在那里你会看到问题的根源:

/* Run iterator to exhaustion. */
for (;;) {

然后你去。

或者至少我是这么认为的...... :)这是一个有趣的问题,所以我想我会有一些乐趣并深入挖掘来源,看看是什么,最后到那里。

关于numpy数组的不同行为,它只会影响numpy.array分配的处理方式。

请注意,使用itertools.repeat在numpy中不起作用,但它不会挂起(我没有检查实现以找出原因):

>>> import numpy, itertools
>>> x = numpy.ones(10,dtype='bool')
>>> x[::2] = itertools.repeat(False)
>>> x
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)
>>> #but the scalar assignment does work as advertised...
>>> x = numpy.ones(10,dtype='bool')
>>> x[::2] = False
>>> x
array([False,  True, False,  True, False,  True, False,  True, False,  True], dtype=bool)

答案 2 :(得分:1)

试试这个:

l = len(x)
x[::2] = itertools.repeat(False, l/2 if l % 2 == 0 else (l/2)+1)

您的原始解决方案最终会进入无限循环,因为这是repeat应该执行的操作,来自documentation

  

创建一个一遍又一遍地返回对象的迭代器。除非指定了times参数,否则无限期运行。

答案 3 :(得分:0)

切片x[::2]的长度恰好为len(x)/2个元素,因此您可以实现所需的效果:

x[::2] = [False]*(len(x)/2)

itertools.repeatitertools.cycle方法旨在无限地产生值。但是,您可以在repeat()上指定限制。像这样:

x[::2] = itertools.repeat(False, len(x)/2)

答案 4 :(得分:0)

扩展切片分配的右侧需要是正确大小的可迭代(在本例中为10)。

以下是右侧的常规列表:

>>> x = [True] * 20
>>> x[::2] = [False] * 10
>>> x
[False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True]

右边是 itertools.repeat

>>> from itertools import repeat
>>> x = [True] * 20
>>> x[::2] = repeat(False, 10)
>>> x
[False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True]