给出一个列表,例如x = [True] * 20,我想为每个其他元素指定False.
x[::2] = False
引发TypeError:必须将iterable分配给扩展切片
所以我天真地认为你可以这样做:
x[::2] = itertools.repeat(False)
要么
x[::2] = itertools.cycle([False])
但是,据我所知,这导致无限循环.为什么会出现无限循环?是否有一种替代方法不涉及在分配之前知道切片中元素的数量?
编辑:我理解x [:: 2] = [False] * len(x)/ 2在这种情况下有效,或者你可以在更一般的情况下为右侧的乘数提出一个表达式.我试图理解是什么导致itertools无限循环,以及为什么列表赋值的行为与numpy数组赋值不同.我认为python必须有一些基本的东西,我误解了.我原本也在考虑可能有性能原因偏好itertools列出理解或创建另一个n元素列表.
解决方法:
正如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)