2010-11-02 15 views
5

Se supone que debo tomar una lista de palabras y contar todas las palabras en ella que tienen 2 o más caracteres y donde el primer y el último carácter son iguales.Comprensión de lista y len() vs. simple for loop

me ocurrió con dos soluciones posibles:

result = 0 
for word in words: 
    if len(word) >= 2 and word[0] == word[-1]: 
     result += 1 
return result 

vs.

return len([word for word in words if len(word) >= 2 and word[0] == word[-1]]) 

Cuál sería la solución preferida? ¿O hay otros mejores?

+0

Tomaría el n. ° 2: pero formateado muy bien en varias líneas. El hecho de que * pueda * escribirlo en una línea no significa que deba (incluso con generadores o for-comp, etc.) –

+0

Por favor, por favor mida. Por favor usa 'timeit' para medir esto. Por favor mida y publique los resultados. –

+1

@ S.Lott: No dijo nada sobre el rendimiento. La forma * preferida * de hacer algo no implica la forma * más rápida * de hacer algo. –

Respuesta

14

En su segundo ejemplo, un generator expression sería mejor que la lista si su lista es grande.

sum(1 for word in words if len(word) >= 2 and word[0] == word[-1]) 
+1

TypeError: objeto de tipo 'generador' no tiene len() – dan04

+0

¡Gracias! Casi iba a sugerirlo. – pyfunc

+0

@ dan04: gracias; arreglado ahora. @pyfunc: gracias! – bernie

1

Ambos son bastante buenos.

hay pequeñas diferencias:

Lista comprensión vuelve otra lista, que está de paso a len. La primera solución evita la creación de otra lista.

3

La primera sería definitivamente la solución preferida en Python.

No se olvide tu Zen de Python:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren't special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you're Dutch.

Now is better than never.

Although never is often better than right now.

If the implementation is hard to explain, it's a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea -- let's do more of those!

Aparte de que sus soluciones son buenas.

+4

La fórmula habitual de lirismo de pitón zen-de-pitón: hacer un reclamo y señalar una línea como justificación, como si fuera un texto religioso. (Encuentro la segunda versión muy clara, la primera parece algo portada de C.) –

+0

Estoy completamente en desacuerdo. Las comprensiones de listas son * muy * Ptónicas y legibles. –

2

Personalmente encuentro que el ciclo explícito es más legible, pero es una cuestión de gusto (algunos prefieren el código más corto en general, especialmente cuando tienen que escribirlo).

versión O se puede acortar aún más/mejora:

result = 0 
for word in words: 
    result += int(len(word) >= 2 and word[0] == word[-1]) 
return result 

El int() conversiones es estrictamente hablando innecesario, ya que verdadero es una especie de 1, pero puede ser mejor para facilitar la lectura. El mismo enfoque se puede aplicar a la comprensión:

return sum(len(word) >= 2 and word[0] == word[-1] for word in words) 

Si desea utilizar len(), me gustaría señalar al lector al hecho de que los valores no importan:

len(1 for word in words if len(word) >= 2 and word[0] == word[-1]) 
+0

'a + = int (bool)' es unidiomático al punto donde lo llamaría malvado. No hagas eso; la condición original es mucho más clara. –

1

Algunas otras variantes es posible que desee considerar:

En primer lugar, puede dividir la condición del filtro en una función. Esta condición es bien de cualquier manera, pero si llega a ser más complejo sin duda me hacer esto:

def check(word): 
    return len(word) >= 2 and word[0] == word[-1] 
sum(1 for word in words if check(word)) 

A continuación, si la generación de una lista (como en la comprensión lista original) es aceptable, entonces usted puede hacer esto :

len(filter(check, words)) 

Hay itertools.ifilter, pero si se utiliza que es necesario utilizar la expresión sum de nuevo, por lo que no termina más claro.

El truco sum aparece tan a menudo que me sorprende que no haya una llamada a la biblioteca estándar para contar la cantidad de elementos en un iterador (si es que no lo he encontrado). Alternativamente, tendría sentido si len consumiría y contaría el número de entradas en un iterador si no tiene __len__, pero no es así.