manuel araoz
Cada día amo más y más a Python.
Hoy, estaba escribiendo un código como:
for i in xrange(N):
do_something()
Tuve que hacer algo N veces. Pero cada vez no dependía del valor de i
(variable índice). Me di cuenta de que estaba creando una variable que nunca usé (i
), y pensé “Seguramente hay una forma más pitónica de hacer esto sin la necesidad de esa variable de índice inútil”.
Entonces… la pregunta es: ¿sabes cómo hacer esta simple tarea de una manera más (pitónica) hermosa?
Un enfoque un poco más rápido que hacer un bucle xrange(N)
es:
import itertools
for _ in itertools.repeat(None, N):
do_something()
-
¿Cuanto más rápido? ¿Todavía hay una diferencia en Python 3.1?
– Hamish Grubijan
4 de junio de 2010 a las 1:18
-
@Hamish: mi prueba con 2,6 dice que es un 32 % más rápido (23,2 us frente a 17,6 us para N = 1000). Pero ese es realmente un tiempo de todos modos. Predeterminaría el código del OP porque es más inmediatamente legible (para mí).
– Mike Bóers
4 de junio de 2010 a las 1:31
-
Es bueno saber sobre la velocidad. Ciertamente me hago eco del sentimiento de Mike acerca de que el código del OP es más legible.
–Wayne Werner
4 de junio de 2010 a las 17:52
-
¿Estás seguro de que la velocidad es realmente relevante? ¿No es así que si hace algo significativo en ese bucle, es muy probable que le tome cientos o miles de tiempo como el estilo de iteración que eligió?
– Henning
5 mayo 2014 a las 19:22
-
Aparte de eso, sí, de alguna manera es más claro ya que usa un método llamado “repetir” para repetir algo. Pero también es una expresión más larga y rara, y también agrega una importancia adicional, que también consume tiempo y recursos. Supongo que en realidad es una decisión muy filosófica, básicamente la razón por la que llegué aquí, buscando una alternativa a la anterior después de haber escrito en un código. Dicho esto, no estoy 100% convencido de cambiar el código ahora…
– Henning
5 mayo 2014 a las 19:37
verde mate
Use la variable _, como aprendí cuando hice esta pregunta, por ejemplo:
# A long way to do integer exponentiation
num = 2
power = 3
product = 1
for _ in xrange(power):
product *= num
print product
-
No es el votante negativo, pero podría deberse a que se está refiriendo a otra publicación en lugar de agregar más detalles en la respuesta.
– Cabra abajo
24 de enero de 2016 a las 5:35
-
@Downgoat: Gracias por los comentarios. Dicho esto, no hay mucho que decir sobre este modismo. Mi punto al referirme a otra publicación fue señalar que una búsqueda podría haber producido la respuesta. Me parece irónico que esta pregunta tenga varias veces más votos a favor que la otra.
– GreenMatt
12/04/2016 a las 17:31
solo uso for _ in range(n)
, va directo al grano. Va a generar la lista completa para números grandes en Python 2, pero si está usando Python 3, no hay problema.
dado que la función es un ciudadano de primera clase, puede escribir un envoltorio pequeño (de las respuestas de Alex)
def repeat(f, N):
for _ in itertools.repeat(None, N): f()
entonces puedes pasar la función como argumento.
Khorkrak
El _ es lo mismo que x. Sin embargo, es un idioma de Python que se usa para indicar un identificador que no tiene la intención de usar. En python, estos identificadores no requieren memoria ni asignan espacio como lo hacen las variables en otros idiomas. Es fácil olvidar eso. Son solo nombres que apuntan a objetos, en este caso un número entero en cada iteración.
japos
Encontré las diversas respuestas realmente elegantes (especialmente las de Alex Martelli), pero quería cuantificar el rendimiento de primera mano, así que preparé el siguiente guión:
from itertools import repeat
N = 10000000
def payload(a):
pass
def standard(N):
for x in range(N):
payload(None)
def underscore(N):
for _ in range(N):
payload(None)
def loopiter(N):
for _ in repeat(None, N):
payload(None)
def loopiter2(N):
for _ in map(payload, repeat(None, N)):
pass
if __name__ == '__main__':
import timeit
print("standard: ",timeit.timeit("standard({})".format(N),
setup="from __main__ import standard", number=1))
print("underscore: ",timeit.timeit("underscore({})".format(N),
setup="from __main__ import underscore", number=1))
print("loopiter: ",timeit.timeit("loopiter({})".format(N),
setup="from __main__ import loopiter", number=1))
print("loopiter2: ",timeit.timeit("loopiter2({})".format(N),
setup="from __main__ import loopiter2", number=1))
También se me ocurrió una solución alternativa que se basa en la de Martelli y utiliza map()
para llamar a la función de carga útil. Bien, hice un poco de trampa porque me tomé la libertad de hacer que la carga útil aceptara un parámetro que se descarta: no sé si hay alguna forma de evitarlo. Sin embargo, aquí están los resultados:
standard: 0.8398549720004667
underscore: 0.8413165839992871
loopiter: 0.7110594899968419
loopiter2: 0.5891903560004721
por lo que el uso de map produce una mejora de aproximadamente un 30 % sobre el bucle for estándar y un 19 % adicional sobre el de Martelli.
cox chen
Suponga que ha definido hacer algo como una función, y le gustaría realizarla norte veces. Tal vez puedas probar lo siguiente:
todos = [do_something] * N
for doit in todos:
doit()
-
Por supuesto. No solo llamemos a la función un millón de veces, también asignemos una lista de un millón de elementos. Si la CPU está funcionando, ¿no debería también estresarse un poco la memoria? La respuesta no se puede caracterizar como definitivamente “no útil” (muestra un enfoque funcional diferente), por lo que no puedo votar negativamente, pero no estoy de acuerdo y me opongo totalmente.
– tzot
5 de junio de 2010 a las 23:59
-
¿No es solo una lista de N referencias al mismo valor de función?
–Nick McCurdy
21 de marzo de 2016 a las 1:49
-
bastante mejor que hacer
fn() for fn in itertools.repeat(do_something, N)
y guarde la generación previa de la matriz… esta es mi expresión preferida.– Rumores F1
10 de mayo de 2016 a las 3:49
-
@tzot ¿Por qué el tono condescendiente? Esta persona se esforzó en escribir una respuesta y ahora puede desanimarse de contribuir en el futuro. Incluso si tiene implicaciones de rendimiento, es una opción de trabajo y, especialmente si N es pequeño, las implicaciones de rendimiento/memoria no son significativas.
– davidcolgan
2 oct 2018 a las 14:41
-
Siempre me sorprende lo obsesionados que están los desarrolladores de Python con el rendimiento 🙂 Aunque estoy de acuerdo en que no es idiomático, y alguien nuevo en Python que lo lea puede no entender lo que sucede tan claramente como cuando simplemente usa un iterador.
-Asfand Qazi
4 de enero de 2019 a las 12:09
Acabo de enterarme de la variable _, pero de lo contrario consideraría la forma en que lo estás haciendo Pythonic. No creo haber visto nunca un bucle for simple hecho de otra manera, al menos en python. Aunque estoy seguro de que hay casos de uso específicos en los que lo miras y dices “Espera, eso se ve terrible”, pero en general, xrange es la forma preferida (hasta donde he visto).
–Wayne Werner
4 de junio de 2010 a las 17:50
Posible duplicado de ¿Es posible implementar un Python para bucle de rango sin una variable de iterador?
– Ciro Santilli Путлер Капут 六四事
24/11/2015 a las 21:00
NOTA: xrange no existe en Python3. Usar
range
en cambio.– John Henkel
7 dic 2016 a las 20:08