Подтвердить что ты не робот

Какова цель в Python itertools.repeat?

Каждое использование, которое я могу придумать для класса Python itertools.repeat(), я могу думать о другом, равно как и, возможно, более приемлемом решении для достижения такого же эффекта. Например:

>>> (i for i in itertools.repeat('example', 5))
('example', 'example', 'example', 'example', 'example')
>>> ('example') * 5
('example', 'example', 'example', 'example', 'example')

>>> map(str.upper, itertools.repeat('example', 5))
['EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE']
>>> ['example'.upper()] * 5
['EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE']

Есть ли какой-нибудь случай, когда это было бы наиболее подходящим решением? Если да, то при каких обстоятельствах?

4b9b3361

Ответ 1

Функция itertools.repeat является ленивой; он использует только память, требуемую для одного элемента. С другой стороны, идиомы (a) * n и [a] * n создают n копий объекта в памяти. Для пяти элементов, идиома умножения, вероятно, лучше, но вы можете заметить проблему с ресурсами, если вам нужно было что-то повторить, скажем, миллион раз.

Тем не менее, трудно представить много статических применений для itertools.repeat. Однако тот факт, что itertools.repeat является функцией, позволяет использовать ее во многих функциональных приложениях. Например, у вас может быть некоторая функция библиотеки func, которая работает с итерабельностью ввода. Иногда у вас могут быть предварительно построенные списки различных предметов. В других случаях вы можете просто использовать единый список. Если список большой, itertools.repeat сохранит вашу память.

Наконец, repeat делает возможной так называемую "итерационную алгебру", описанную в документации itertools. Даже сам модуль itertools использует функцию repeat. Например, следующий код указан как эквивалентная реализация itertools.izip_longest (хотя реальный код, вероятно, написан на C). Обратите внимание на использование repeat семи строк снизу:

class ZipExhausted(Exception):
    pass

def izip_longest(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    counter = [len(args) - 1]
    def sentinel():
        if not counter[0]:
            raise ZipExhausted
        counter[0] -= 1
        yield fillvalue
    fillers = repeat(fillvalue)
    iterators = [chain(it, sentinel(), fillers) for it in args]
    try:
        while iterators:
            yield tuple(map(next, iterators))
    except ZipExhausted:
        pass

Ответ 2

Ваш пример foo * 5 внешне внешне похож на itertools.repeat(foo, 5), но на самом деле он совсем другой.

Если вы пишете foo * 100000, интерпретатор должен создать 100 000 экземпляров foo, прежде чем он сможет дать вам ответ. Таким образом, это очень дорогое и недружелюбное функционирование.

Но если вы пишете itertools.repeat(foo, 100000), интерпретатор может вернуть итератор, который обслуживает одну и ту же функцию, и не нужно вычислять результат, пока он вам не понадобится, например, используя его в функции, которая хочет знать каждый результат в последовательности.

Это главное преимущество итераторов: они могут отложить вычисление части (или всего) списка, пока вам не понадобится ответ.

Ответ 3

Основная цель itertools.repeat - предоставить поток постоянных значений, которые будут использоваться с картой или zip:

>>> list(map(pow, range(10), repeat(2)))     # list of squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Вторичная цель заключается в том, что он дает очень быстрый способ циклического цикла фиксированного числа раз:

for _ in itertools.repeat(None, 10000):
    do_something()

Это быстрее, чем:

for i in range(10000):
    do_something().

Бывший выигрывает, потому что все, что ему нужно сделать, это обновить счетчик ссылок для существующего объекта None. Последний теряет, потому что диапазон() или xrange() должен производить 10 000 различных целых объектов.

Примечание. Сам Гвидо использует эту технику быстрого цикла в модуле timeit(). См. Источник в https://hg.python.org/cpython/file/2.7/Lib/timeit.py#l195:

    if itertools:
        it = itertools.repeat(None, number)
    else:
        it = [None] * number
    gcold = gc.isenabled()
    gc.disable()
    try:
        timing = self.inner(it, self.timer)

Ответ 4

Это итератор. Большая подсказка здесь: это в модуле itertools. Из документации, к которой вы привязались:

itertools.repeat(объект [, раз])Создайте iterator, который возвращает объект снова и снова. Выполняется неопределенно, если не указан аргумент times.

Итак, у вас никогда не будет всего этого в памяти. Пример, в котором вы хотите использовать его, может быть

n = 25
t = 0
for x in itertools.repeat(4):
    if t > n:
        print t
    else:
        t += x

так как это позволит вам произвольное количество 4 s или что-то еще, что вам может понадобиться для бесконечного списка.

Ответ 5

Как упоминалось ранее, он хорошо работает с zip:

Другой пример:

from itertools import repeat

fruits = ['apples', 'oranges', 'bananas']

# Initialize inventory to zero for each fruit type.
inventory = dict( zip(fruits, repeat(0)) )

Результат:

{'apples': 0, 'oranges': 0, 'bananas': 0}

Чтобы сделать это без повторения, мне пришлось бы включать len(fruits).

Ответ 6

Я обычно использую повтор в сочетании с цепочкой и циклом. Вот пример:

from itertools import chain,repeat,cycle

fruits = ['apples', 'oranges', 'bananas', 'pineapples','grapes',"berries"]

inventory = list(zip(fruits, chain(repeat(10,2),cycle(range(1,3)))))

print inventory

Поместит первые 2 плода в качестве значения 10, затем он циклирует значения 1 и 2 для остальных фруктов.