itertools и назначение списка изменений - PullRequest
3 голосов
/ 01 декабря 2011

Имеется список, например 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 работать бесконечно и почему назначение списка ведет себя не так, как присвоение массива. Я думаю, что в питоне должно быть что-то фундаментальное, что я неправильно понимаю. Первоначально я также думал, что могут быть причины производительности для предпочтения itertools составлять списки или создавать другой список из n элементов.

Ответы [ 5 ]

3 голосов
/ 01 декабря 2011

То, что вы пытаетесь сделать в этом коде, не то, что вы думаете (я подозреваю), например:
x[::2] вернет фрагмент, содержащий каждый odd элемент x, поскольку 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 edit

В документации о цикле говорится, что это 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, затем нулевой список, потому что генератор израсходован.

2 голосов
/ 01 декабря 2011

Как отметил Марк Толонен в кратком комментарии, причина, по которой ваши попытки 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, но не зависает (я не проверял реализацию, чтобы выяснить, почему):

>>> 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)
1 голос
/ 01 декабря 2011

Попробуйте это:

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

Ваше оригинальное решение заканчивается бесконечным циклом, потому что именно это и должно делать repeat из документации :

Создайте итератор, который возвращает объект снова и снова. Работает бесконечно, если не указан аргумент times.

0 голосов
/ 01 декабря 2011

Правая часть расширенного назначения среза должна быть итерируемой правильного размера (в данном случае десять).

Вот это с обычным списком справа:

>>> 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]
0 голосов
/ 01 декабря 2011

Срез x[::2] имеет длину ровно len(x)/2 элементов, поэтому вы можете достичь желаемого с помощью:

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

Методы itertools.repeat и itertools.cycle предназначены для получения значений бесконечно. Однако вы можете указать ограничение на repeat(). Как это:

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