example - ¿Cuál es el propósito en itertools.repeat de Python?
product() python (6)
Como se mencionó anteriormente, funciona bien con zip
:
Otro ejemplo:
from itertools import repeat
fruits = [''apples'', ''oranges'', ''bananas'']
# Initialize inventory to zero for each fruit type.
inventory = dict( zip(fruits, repeat(0)) )
Resultado:
{''apples'': 0, ''oranges'': 0, ''bananas'': 0}
Para hacer esto sin repetición, tendría que involucrar a len(fruits)
.
Cada vez que puedo pensar en la clase itertools.repeat()
Python, se me ocurre otra solución igualmente aceptable (posiblemente más) para lograr el mismo efecto. Por ejemplo:
>>> (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'']
¿Hay algún caso en el que sería la solución más adecuada? De ser así, ¿bajo qué circunstancias?
El propósito principal de itertools.repeat es proporcionar un flujo de valores constantes para usar con map o zip :
>>> list(map(pow, range(10), repeat(2))) # list of squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
El propósito secundario es que proporciona una forma muy rápida de realizar un bucle de un número fijo de veces como esta:
for _ in itertools.repeat(None, 10000):
do_something()
Esto es más rápido que:
for i in range(10000):
do_something().
El primero gana porque todo lo que necesita hacer es actualizar el recuento de referencia para el objeto Ninguno existente. Este último pierde porque el rango () o xrange () necesita fabricar 10,000 objetos enteros distintos.
Tenga en cuenta que el propio Guido utiliza esa técnica de bucle rápido en el módulo timeit() . Consulte la fuente en 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)
Es un iterador. Gran pista aquí: está en el módulo de itertools. De la documentación que has vinculado a:
itertools.repeat (objeto [, veces]) Crea un iterador que devuelve el objeto una y otra vez. Se ejecuta indefinidamente a menos que se especifique el argumento times.
Así que nunca tendrás todas esas cosas en la memoria. Un ejemplo donde quieras usarlo podría ser
n = 25
t = 0
for x in itertools.repeat(4):
if t > n:
print t
else:
t += x
ya que esto te permitirá un número arbitrario de 4
s, o lo que necesites, una lista infinita de.
La función itertools.repeat
es perezosa; solo utiliza la memoria requerida para un elemento. Por otro lado, los modismos (a) * n
y [a] * n
crean n copias del objeto en la memoria. Para cinco elementos, el lenguaje de multiplicación es probablemente mejor, pero es posible que note un problema de recursos si tuviera que repetir algo, por ejemplo, un millón de veces.
Aún así, es difícil imaginar muchos usos estáticos para itertools.repeat
. Sin embargo, el hecho de que itertools.repeat
sea una función le permite utilizarla en muchas aplicaciones funcionales. Por ejemplo, es posible que tenga alguna función de func
biblioteca que funcione en una entrada iterable. A veces, es posible que tenga listas preconstruidas de varios elementos. Otras veces, es posible que desee operar en una lista uniforme. Si la lista es grande, itertools.repeat
te ahorrará memoria.
Finalmente, repeat
hace posible el llamado "álgebra de iteradores" descrito en la documentación de itertools
. Incluso el itertools
módulo itertools
utiliza la función de repeat
. Por ejemplo, el siguiente código se proporciona como una implementación equivalente de itertools.izip_longest
(aunque probablemente el código real esté escrito en C). Tenga en cuenta el uso de repeat
siete líneas desde la parte inferior:
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
Su ejemplo de foo * 5
parece superficialmente similar a itertools.repeat(foo, 5)
, pero en realidad es bastante diferente.
Si escribe foo * 100000
, el intérprete debe crear 100,000 copias de foo
antes de que pueda darle una respuesta. Por lo tanto, es una operación muy costosa y hostil para la memoria.
Pero si escribe itertools.repeat(foo, 100000)
, el intérprete puede devolver un iterador que cumple la misma función y no necesita calcular un resultado hasta que lo necesite, por ejemplo, usándolo en una función que desee Para saber cada resultado en la secuencia.
Esa es la principal ventaja de los iteradores: pueden diferir el cálculo de una parte (o todas) de una lista hasta que realmente necesite la respuesta.
Suelo usar repetición junto con cadena y ciclo. Aquí hay un ejemplo:
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
Pone las 2 primeras frutas como valor 10, luego alterna los valores 1 y 2 para las frutas restantes.