Да, это так, как вы говорите (за исключением того, что вы не копируете список):
Это говорит о том, чтобы преобразовать генератор в список, скопировать его и преобразовать оба списка в итераторы/ генераторы?
Допустим, у вас есть генератор и вы хотите сделать пять его копий.Каждая из этих копий должна давать те же значения, что и ваш оригинальный генератор.Простым решением было бы получить все значения из вашего генератора, например, преобразовав его в список, а затем используя этот список для создания новых генераторов:
def orig(n):
yield from range(n)
orig_gen = orig(100)
for i in range(90):
next(orig_gen)
# now we have 10 values left in gen
values_left = list(orig_gen)
def copy():
yield from values_left
copy_gen1 = copy()
copy_gen2 = copy()
print(next(copy_gen1))
print(next(copy_gen2))
Хотя это может стать очень дорогим.Целью генератора является создание новых значений динамически.Если вы преобразуете генератор в список, вы должны выполнить все вычисления, необходимые для получения этих значений.Кроме того, если генератор выдает много значений, вы в конечном итоге будете использовать огромное количество памяти.
Именно поэтому tee()
предлагает буферизованный подход.Вы должны указать, сколько копий генератора вы хотите, а затем tee()
устанавливает deque (список с быстрыми добавлениями и всплывающими окнами) для каждой копии.Когда вы запрашиваете новое значение у одного из скопированных генераторов, оно берется из буфера.Только если буфер пуст, новые значения генерируются из исходного генератора.Источник указан в документах .Допустим, вы хотите получить 5 копий, используя tee()
, это может выглядеть так:
- создайте оригинальный генератор и используйте его немного
- создайте 5 копий с
tee().
каждаякопия имеет пустой буфер - вызов
next()
для копии 1. Поскольку буфер пуст, мы вызываем next()
в исходном генераторе.Значение добавляется ко всем буферам.Он немедленно извлекается из буфера 1 и возвращает - call
next()
для копии 2. Буфер для копии 2 уже содержит это значение, мы извлекаем его из буфера и возвращаем его.Не нужно использовать оригинальный генератор. - вызов
next()
для копии 1. На предыдущем шаге нам не нужно было использовать оригинальный генератор, поэтому наш буфер все еще пуст.Мы называем next()
на оригинальном генераторе.Значение добавляется ко всем буферам.Он немедленно вынимается из буфера 1 и возвращается - , вызывая
next()
в копии 3 дважды.Оба раза мы можем просто прочитать значение из буфера.
Если вы вызываете next()
для копий с примерно одинаковой частотой, это очень эффективно.Буферы не растут, так как значения удаляются из них равномерно.Таким образом, вам не нужно хранить много значений в памяти.
Но если вы используете только одну из копий, то буферы для других копий становятся все больше и больше.В крайнем случае, если вы исчерпаете копию 1, прежде чем вы коснетесь других копий, их буферы станут списками со всеми значениями из генератора.Теперь у вас есть 4 списка со всеми значениями.Вместо простого списка со всеми значениями в простом подходе.