给出一个列表,例如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元素列表。
答案 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.repeat
和itertools.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]