3

给定一个列表,例如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 元素列表。

4

5 回答 5

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编辑

关于循环的文档说明Repeats indefinitely.了这一点,这意味着它将通过给定的迭代器不断“循环”。

重复具有类似的实现,但是文档指出它
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,然后打印一个空列表,因为生成器已用完。

于 2011-12-01T04:35:55.240 回答
2

正如 Mark Tolonen 在简明评论中指出的那样,您的 itertools 尝试无限期循环的原因是,对于列表分配,python 正在检查右侧的长度。

现在要真正深入...

当你说:

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

左侧 ( x[::2]) 是一个列表,您正在为一个列表分配一个值,其中该值是可迭代的,由于没有给出长度(根据文档itertools.repeat(False),它将永远迭代。

如果您深入研究 cPython 实现中的列表分配代码,您会发现不幸/痛苦命名的函数list_ass_slice,它是许多列表分配内容的根源。在该代码中,您将看到此段

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,它最终尝试将您的可迭代对象转换为列表(使用PySequence_List),最终在其中创建一个空列表并尝试使用您的可迭代对象简单地扩展它。

要使用可迭代扩展列表,它使用listextend(),您将在其中看到问题的根源:

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

你去吧。

或者至少我是这么认为的...... :) 这是一个有趣的问题,所以我想我会找点乐子并挖掘源代码以查看发生了什么,并最终到达那里。

至于 numpy 数组的不同行为,这只是numpy.array分配处理方式的不同。

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

>>> 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)
于 2011-12-01T07:46:35.897 回答
1

试试这个:

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

您的原始解决方案最终陷入无限循环,因为这是文档repeat中应该做的事情:

制作一个反复返回对象的迭代器。除非指定了 times 参数,否则无限期运行。

于 2011-12-01T04:37:36.243 回答
0

切片x[::2]恰好是len(x)/2元素长,因此您可以通过以下方式实现您想要的:

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

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

x[::2] = itertools.repeat(False, len(x)/2)
于 2011-12-01T04:42:47.523 回答
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]
于 2011-12-01T05:40:31.923 回答