Какова цель в Python itertools.repeat? - PullRequest
22 голосов
/ 30 января 2012

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

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

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

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

Ответы [ 6 ]

19 голосов
/ 01 февраля 2012

Основная цель itertools.repeat - предоставить поток постоянных значений для использования с map или 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().

Прежние победыпотому что все, что ему нужно сделать, это обновить счетчик ссылок для существующего Нет объекта.Последний проигрывает, потому что range () или 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)
19 голосов
/ 30 января 2012

Функция 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
14 голосов
/ 30 января 2012

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

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

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

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

2 голосов
/ 27 ноября 2013

Как упоминалось ранее, он хорошо работает с 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).

2 голосов
/ 30 января 2012

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

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

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

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

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

0 голосов
/ 14 февраля 2017

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

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 для оставшихся фруктов.

...