Невозможно «сбросить» генератор. Однако вы можете использовать itertools.tee
для «копирования» итератора.
>>> z = zip(a, b)
>>> zip1, zip2 = itertools.tee(z)
>>> list(zip1)
[(1, 7), (2, 8), (3, 9)]
>>> list(zip2)
[(1, 7), (2, 8), (3, 9)]
Это включает в себя кеширование значений, поэтому имеет смысл только в том случае, если вы повторяете оба итератора примерно с той же скоростью. (Другими словами, не используйте его так, как я здесь!) [/ G8]
Еще один подход - передать функцию генератора и называть его всякий раз, когда вы хотите его итерации.
def gen(x):
for i in range(x):
yield i ** 2
def make_two_lists(gen):
return list(gen()), list(gen())
Но теперь вы должны привязать аргументы к функции генератора при ее передаче. Вы можете использовать lambda
для этого, но многие люди находят lambda
уродливым. (Не я, хотя! YMMV.)
>>> make_two_lists(lambda: gen(10))
([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
Надеюсь, само собой разумеется, что в большинстве случаев лучше просто составить список и скопировать его.
Также, как более общий способ объяснения этого поведения, рассмотрите это. Точка генератора должна производить ряд значений, сохраняя при этом некоторое состояние между итерациями. Теперь, иногда, вместо простого итерации по генератору, вы можете сделать что-то вроде этого:
z = zip(a, b)
while some_condition():
fst = next(z, None)
snd = next(z, None)
do_some_things(fst, snd)
if fst is None and snd is None:
do_some_other_things()
Предположим, что этот цикл может или выхлоп z
. Теперь у нас есть генератор в неопределенном состоянии! Поэтому на данном этапе важно, чтобы поведение генератора сдерживалось четко определенным образом. Хотя мы не знаем, где находится генератор, мы знаем, что: а) все последующие обращения будут давать более поздние значения в серии, и б) после того, как он «пуст», мы получили все предметы в серии ровно один раз. Чем больше мы должны манипулировать состоянием z
, тем сложнее рассуждать об этом, поэтому лучше избегать ситуаций, которые нарушают эти два обещания.
Конечно, как указывает Джоэл Корнетт, ниже можно написать генератор, который принимает сообщения через метод send
; и можно было бы написать генератор, который можно было бы сбросить с помощью send
. Но учтите, что в этом случае мы можем только отправить сообщение . Мы не можем напрямую манипулировать состоянием генератора, и поэтому все изменения в состоянии генератора четко определены (самим генератором - при условии, что оно написано правильно!). send
действительно предназначен для реализации сопрограмм , поэтому я бы не использовал его для этой цели. Каждый день генераторы почти никогда ничего не делают со значениями, присланными им - я думаю по тем причинам, которые я приводил выше.
Альтернатива без IF
В дополнение к правильному решению Скотта, я демонстрирую подход с помощью функции REPT
. Применяемый к ячейке , содержащей строку , он «повторяет» свое содержимое & amp; однажды запятая (обозначается COUNTA()
, равным 1), тогда как пустая ячейка приводит к нулевому повторению, что позволяет пропускать не только содержимое ячейки, но и запятую:
="(" & SUBSTITUTE(REPT(A1&",",COUNTA(A1))&REPT(B1&",",COUNTA(B1))&REPT(C1&",",COUNTA(C1))&REPT(D1&",",COUNTA(D1))&REPT(E1&",",COUNTA(E1))&REPT(F1&",",COUNTA(F1))&"$",",$",")")
[119 ] Простое SUBSTITUTE
удаляет последнюю запятую, где бы она ни находилась перед закрывающей скобкой ")".
Если у вас Office 365 Excel, используйте TEXTJOIN:
="(" & TEXTJOIN(", ",TRUE,A2:F2) & ")"
Если нет, вам нужно будет использовать IF для каждого возврата и Mid:
=MID("(" & IF(A2 <> "",", " & A2,"") & IF(A2 <> "",", " & B2,"") & IF(C2 <> "",", " & C2,"") & IF(D2 <> "",", " & D2,"") & IF(E2 <> "",", " & E2,"") & IF(F2 <> "",", " & F2,""),3,999)