Tengo esa linea:

if something in ["name_1", "name_2", "name_3", "name_4", "name_5"]

Y me pregunto si puedo escribirlo más corto usando el formato. Sé que puedo escribirlo así:

if something in ["name_%d" %(1), "name_%d" %(2), "name_%d" %(3)]:

Pero debido a que esas cadenas tienen la misma parte "nombre_", esperaba que haya una forma más corta de formateo, similar a esta (esto no funciona):

if something in ["name_%d" %(1, 2, 3, 4, 5)]:

¿Alguna idea?

0
Natalia 31 oct. 2017 a las 13:29

3 respuestas

La mejor respuesta

Puede generar su lista con una comprensión de la lista fácilmente:

if something in ['name_%d' % i for i in range(1, 6)]:

Sin embargo , en realidad solo está buscando un patrón de texto; una expresión regular puede probar eso de manera más eficiente:

import re

if re.match(r'name_[1-6]$', something):

O simplemente podría probar si la cadena comienza con un nombre:

if something.startswith('name_') and something[5:] in {'1', '2', '3', '4', '5'}:

En Python 3, usar un static set literal para probar la membresía será más rápido que usar una lista. Ambos se almacenan en caché en el código de bytes como estructuras inmutables, pero la prueba contra un conjunto requiere O (1) tiempo constante, frente a O (N) para obtener una lista. Sin embargo, si está generando el conjunto o la lista, aún paga un costo O (N) solo para construir ese objeto. Generar un objeto de lista es más rápido (no se requiere hashing), lo que hace que sea un poco más rápido probarlo. Una expresión generadora es aún más lenta.

Comparaciones de tiempos para los enfoques de comprensión (expresiones generadoras, comprensiones de conjuntos y listas, para el primer, último y casos fallidos):

>>> import timeit
>>> timeit.timeit('if something in ("name_{}".format(i) for i in range(1, 6)): pass',
...               'something="name1"')
2.0784148779930547
>>> timeit.timeit('if something in {"name_{}".format(i) for i in range(1, 6)}: pass',
...               'something="name1"')
2.032067227992229
>>> timeit.timeit('if something in ["name_{}".format(i) for i in range(1, 6)]: pass',
...               'something="name1"')
1.9060910780681297
>>> timeit.timeit('if something in ("name_{}".format(i) for i in range(1, 6)): pass',
...               'something="name5"')
2.1426312710391358
>>> timeit.timeit('if something in {"name_{}".format(i) for i in range(1, 6)}: pass',
...               'something="name5"')
2.0627736690221354
>>> timeit.timeit('if something in ["name_{}".format(i) for i in range(1, 6)]: pass',
...               'something="name5"')
1.9719348540529609
>>> timeit.timeit('if something in ("name_{}".format(i) for i in range(1, 6)): pass',
...               'something="name42"')
2.160375243984163
>>> timeit.timeit('if something in {"name_{}".format(i) for i in range(1, 6)}: pass',
...               'something="name42"')
2.0166494220029563
>>> timeit.timeit('if something in ["name_{}".format(i) for i in range(1, 6)]: pass',
...               'something="name42"')
2.0706132350023836

Por solo 5 artículos, es un poco un lavado, con quizás la comprensión de la lista ganadora.

Para más de 1000 nombres posibles:

>>> timeit.timeit('if something in ("name_{}".format(i) for i in range(1, 1001)): pass',
...               'something="name1"', number=10**4)
3.895413015037775
>>> timeit.timeit('if something in {"name_{}".format(i) for i in range(1, 1001)}: pass',
...               'something="name1"', number=10**4)
3.459794587106444
>>> timeit.timeit('if something in ["name_{}".format(i) for i in range(1, 1001)]: pass',
...               'something="name1"', number=10**4)
3.510508105973713
>>> timeit.timeit('if something in ("name_{}".format(i) for i in range(1, 1001)): pass',
...               'something="name1000"', number=10**4)
3.792039962951094
>>> timeit.timeit('if something in {"name_{}".format(i) for i in range(1, 1001)}: pass',
...               'something="name1000"', number=10**4)
3.859958241926506
>>> timeit.timeit('if something in ["name_{}".format(i) for i in range(1, 1001)]: pass',
...               'something="name1000"', number=10**4)
3.561700245947577
>>> timeit.timeit('if something in ("name_{}".format(i) for i in range(1, 1001)): pass',
...               'something="name1009"', number=10**4)
3.616139759076759
>>> timeit.timeit('if something in {"name_{}".format(i) for i in range(1, 1001)}: pass',
...               'something="name1009"', number=10**4)
3.4787185511086136
>>> timeit.timeit('if something in ["name_{}".format(i) for i in range(1, 1001)]: pass',
...               'something="name1009"', number=10**4)
3.2148393219104037

La comprensión de la lista todavía tiene la ventaja, por un pequeño margen.

Sin embargo, usar una expresión regular es fácilmente más rápido que eso, por un factor de 3:

>>> timeit.timeit('if re.match(r"name_[1-5]$", something): pass',
...               'import re; something="name1"')
0.7225337530253455
>>> timeit.timeit('if re.match(r"name_[1-5]$", something): pass',
...               'import re; something="name5"')
0.7184386339504272
>>> timeit.timeit('if re.match(r"name_[1-5]$", something): pass',
...               'import re; something="name42"')
0.7749457659665495

Esto se puede hacer aún más rápido almacenando en caché el resultado re.compile(..).

La ganadora clara es simple coincidencia de texto:

>>> timeit.timeit('if something.startswith("name_") and something[5:] in {"1", "2", "3", "4", "5"}: pass',
...               'something="name1"')
0.15361014590598643
>>> timeit.timeit('if something.startswith("name_") and something[5:] in {"1", "2", "3", "4", "5"}: pass',
...               'something="name5"')
0.14619109802879393
>>> timeit.timeit('if something.startswith("name_") and something[5:] in {"1", "2", "3", "4", "5"}: pass',
...               'something="name42"')
0.1544568829704076

Esto es aproximadamente 15 veces más rápido que la prueba de comprensión de listas más rápida.

1
Martijn Pieters 31 oct. 2017 a las 13:10

Pruebe lo siguiente:

if something in ["name_{}".format(i) for i in range(1, 6)]:

Utiliza la comprensión de la lista, la función range(start, end) (que incluye el inicio pero no el final) y str.format(), todo bastante pitónico.

El rango por defecto comienza en 0, por lo que también podría elegir:

if something in ["name_{}".format(i+1) for i in range(5)]:

También sugeriría cambiar la lista por una expresión generadora:

if something in ("name_{}".format(i) for i in range(1, 6)):

if something in ("name_{}".format(i+1) for i in range(5)):
2
Adirio 31 oct. 2017 a las 10:39

Usted puede hacer:

if something in ['name_'+str(i+1) for i in range(5)]
-1
Meet Taraviya 31 oct. 2017 a las 10:54