2010-03-08 23 views
40

tonta pregunta:
que tiene un simple bucle seguido de un estado simple si:¿Sola línea para el iterador de bucle con un filtro "si"?

for airport in airports: 
    if airport.is_important: 

y me preguntaba si puedo escribir esto como una sola línea de alguna manera.
Así que, sí, yo puedo hacer esto:

for airport in (airport for airport in airports if airport.is_important): 

pero lee de manera tonta y redundante (for airport in airport for airport in airports...).
¿Hay una manera mejor?

+9

Si no le gusta tener tantos "aeropuertos" en una línea, canjee tres de ellos por un solo carácter: 'para el aeropuerto en (x para x en los aeropuertos si x.is_important): ' – MattH

+12

Me pregunto por qué Python no tiene' para aeropuerto en aeropuertos si airport.is_important: ', parece útil y claro. – Mark

+1

También puede usar 'if not airport.is_important: continue' para evitar sangrías. Pero me gusta más la respuesta aceptada, así que no sé por qué escribí este comentario. – Moberg

Respuesta

57

No, no hay forma más corta. Por lo general, incluso se va a romper en dos líneas:

important_airports = (airport for airport in airports if airport.is_important) 
for airport in important_airports: 
    # do stuff 

Esto es más flexible, más fácil de leer y todavía no consumen mucha memoria.

+12

+1: simplemente escriba lo que quiere decir. 1-liner Code Golf no es una buena práctica. –

+3

Gracias, realmente me gusta esta respuesta. Aunque no es un trazador de líneas único, 'capta el espíritu' del trazador de líneas en que hay una sola sangría (¿puedo decir que odio las indentaciones mientras sigo amando a Python?). No hay necesidad de importaciones adicionales y al usar un nombre de variable adecuado, la segunda línea es perfectamente legible (a quién le importa cómo implementé 'aeropuertos importantes'). Esta es mi respuesta. ¡Gracias de nuevo por la pronta respuesta! –

+0

@TalWeiss Por supuesto, se te permite odiar las sangrías, ¡yo también! – Moberg

5

Mabe esto, pero es más o menos la misma detallado ...

import itertools 

for airport in itertools.ifilter(lambda x: x.is_important, airports): 
    ... 
+0

Sí, esto es lo mejor que pude pensar también. –

+1

FYI - http://codepad.org/e9pOMxV8 –

+0

@tusbar ¿y qué? el op está pidiendo alternativas más legibles, no más eficientes ... y la diferencia es insignificante (~ + -20%), si nos preocupamos por los márgenes estaríamos programando en C, no en Python :-p – fortran

21

que podría hacer

for airport in filter(lamdba x: x.is_important, airports): 
    # do stuff... 
+0

Dependiendo del tamaño de la lista, o más bien del número de aeropuertos importantes, creo que esta es la mejor manera . Es fácil de leer, una vez que has descubierto 'lambda'. – devsnd

+0

Desde Python 2.4 (circa 2002) puede usar una [expresión del generador] (https://docs.python.org/3/reference/expressions.html#generator-expressions) y escribir 'para el aeropuerto en (aeropuerto para el aeropuerto en aeropuertos si airport.is_important): 'que hace que el uso de' filter() 'para este propósito sea algo obsoleto. – martineau

0

Utilizando lista por comprensión (sólo si los aeropuertos es una lista de objetos):

for airport in [a for a in airports if a.is_important]: 
+1

esto está completamente mal, no hace lo que el OP está pidiendo, itera sobre los valores booleanos ... básicamente, ¡es un mapa, no un filtro! – fortran

+1

iterará sobre una lista de bools. – wRAR

+0

sí, estás en lo correcto. Simplemente lo arreglé :) – rogeriopvl

1

Esta es una filosofía de diseño de python. Si le toma demasiadas palabras para ponerlo en una línea, debe dividirlo en pocas líneas para ayudar a la persona que lo sigue. Las expresiones de listas y generadores son más para transformar iterables in situ, formando formas más legibles de map y filter.

11

Usaría un protector negativo en el lazo. Es legible y no introduce un nivel extra de sangría.

for airport in airports: 
    if not airport.is_important: continue 
    <body of loop> 
1

Aquí es una alternativa a algunas de las otras versiones de filtro:

from operator import attrgetter as attr 
for airport in filter(attr('is_important'), airports): 
    ... 

Esto tiene la ventaja de ser muy concisa y también que le permite usar la notación de punto attr ('first_class.is_full').

También podría poner algo así (o una versión con una lista de comprensión) en una función de utilidad como filter_by_attr. Posteriormente, se podría hacer:

for airport in filter_by_attr(airports, 'is_important'): 
    ... 

sigo pensando e-satis es correcta de ponerlo en una nueva variable no importa el método que utilice, sin embargo. Es más claro de esa manera, especialmente si el uso no coincide exactamente con el nombre del atributo en cuestión (o si el criterio es más complejo).

Mi única observación al respecto sería que si te encuentras usando esto en varios lugares, tal vez deberías hacer que los aeropuertos sean una colección especial con 'important_airports' como @property que devuelve la colección filtrada. O algún tipo de abstracción para ocultar el filtrado (como una llamada de servicio).

Cuestiones relacionadas