Me encontré con una necesidad básica de filtrado: tengo una lista y tengo que filtrarla por un atributo de los elementos.
Mi código se veía así:
my_list = [x for x in my_list if x.attribute == value]
Pero luego pensé, ¿no sería mejor escribirlo así?
my_list = filter(lambda x: x.attribute == value, my_list)
Es más legible, y si es necesario para el rendimiento, se podría sacar la lambda para obtener algo.
La pregunta es: ¿hay alguna advertencia al usar la segunda forma? ¿Alguna diferencia de rendimiento? ¿Me estoy perdiendo el Pythonic Way ™ por completo y debería hacerlo de otra manera (como usar itemgetter en lugar de lambda)?

filterera más legible. Cuando tiene una expresión simple que se puede usar como está en una listacomp, pero tiene que estar envuelta en una lambda (o construida de manera similar a partir departialooperatorfunciones, etc.) para pasarfilter, entonces es cuando ganan las listas.filteres un objeto generador de filtro, no una lista.Respuestas:
Es extraño cuánto varía la belleza para diferentes personas. Encuentro la comprensión de la lista mucho más clara que
filter+lambda, pero use la que le resulte más fácil.Hay dos cosas que pueden retrasar su uso
filter.El primero es la sobrecarga de la llamada de función: tan pronto como use una función de Python (ya sea creada por
defolambda) es probable que el filtro sea más lento que la comprensión de la lista. Es casi seguro que no sea suficiente, y no debe pensar mucho en el rendimiento hasta que haya cronometrado su código y haya encontrado que es un cuello de botella, pero la diferencia estará ahí.La otra sobrecarga que podría aplicarse es que la lambda se ve obligada a acceder a una variable de ámbito (
value). Eso es más lento que acceder a una variable local y en Python 2.x la comprensión de la lista solo accede a las variables locales. Si está utilizando Python 3.x, la comprensión de la lista se ejecuta en una función separada, por lo que también se accederá avaluetravés de un cierre y esta diferencia no se aplicará.La otra opción a considerar es usar un generador en lugar de una lista de comprensión:
Luego, en su código principal (que es donde la legibilidad realmente importa) ha reemplazado tanto la comprensión de la lista como el filtro con un nombre de función con significado significativo.
fuente
[]a(). Además, estoy de acuerdo en que el borrador de la lista es más hermoso.filterque es más rápido usando una función de devolución de llamada de Python.Este es un tema algo religioso en Python. A pesar de que Guido consideró eliminarlo
map,filteryreducede Python 3 , hubo suficiente reacción violenta que al final soloreducese movió de las funciones integradas a functools.reduce .Personalmente, encuentro las comprensiones de listas más fáciles de leer. Es más explícito lo que está sucediendo en la expresión,
[i for i in list if i.attribute == value]ya que todo el comportamiento está en la superficie, no dentro de la función de filtro.No me preocuparía demasiado la diferencia de rendimiento entre los dos enfoques, ya que es marginal. Realmente solo optimizaría esto si resultara ser el cuello de botella en su aplicación, lo cual es poco probable.
Además, dado que el BDFL quería
filterpasar del lenguaje, seguramente eso automáticamente hace que las comprensiones de listas sean más pitónicas ;-)fuente
Dado que cualquier diferencia de velocidad está destinada a ser minúscula, usar filtros o comprender las listas se reduce a una cuestión de gustos. En general, me inclino a usar las comprensiones (que parece estar de acuerdo con la mayoría de las otras respuestas aquí), pero hay un caso en el que prefiero
filter.Un caso de uso muy frecuente es extraer los valores de algún X iterable sujeto a un predicado P (x):
pero a veces quieres aplicar alguna función a los valores primero:
Como ejemplo específico, considere
Creo que esto se ve un poco mejor que usar
filter. Pero ahora consideraEn este caso queremos
filtercontra el valor post-calculado. Además del problema de calcular el cubo dos veces (imagine un cálculo más costoso), está el problema de escribir la expresión dos veces, violando la estética DRY . En este caso, sería apto para usarfuente
[prime(i) for i in [x**3 for x in range(1000)]]x*x*xno puede ser un número primo, como lo ha sidox^2yxcomo factor, el ejemplo realmente no tiene sentido de manera matemática, pero tal vez aún sea útil. (Tal vez podríamos encontrar algo mejor, aunque?)prime_cubes = filter(prime, (x*x*x for x in range(1000)))prime_cubes = [1]ahorrar tanto memoria como ciclos de CPU ;-)[]Aunque
filterpuede ser la "forma más rápida", la "forma pitónica" sería no preocuparse por esas cosas a menos que el rendimiento sea absolutamente crítico (¡en cuyo caso no estaría usando Python!).fuente
Pensé que solo agregaría que en Python 3, filter () es en realidad un objeto iterador, por lo que tendría que pasar su llamada al método de filtro a list () para construir la lista filtrada. Entonces en Python 2:
las listas byc tienen los mismos valores, y se completaron aproximadamente al mismo tiempo que filter () era equivalente [x para x en y si z]. Sin embargo, en 3, este mismo código dejaría la lista c que contiene un objeto de filtro, no una lista filtrada. Para producir los mismos valores en 3:
El problema es que list () toma un iterable como argumento y crea una nueva lista a partir de ese argumento. El resultado es que usar el filtro de esta manera en Python 3 toma hasta el doble de tiempo que el método [x para x en y si z] porque debe iterar sobre la salida del filtro (), así como la lista original.
fuente
Una diferencia importante es que la comprensión de la lista devolverá un
listtiempo, mientras que el filtro devuelve unfilter, que no puede manipular como unlist(es decir, invocarlolen, que no funciona con el retorno defilter).Mi propio autoaprendizaje me llevó a un problema similar.
Dicho esto, si hay una manera de obtener el resultado
listde unfilter, un poco como lo haría en .NET cuando lo hagalst.Where(i => i.something()).ToList(), tengo curiosidad por saberlo.EDITAR: Este es el caso de Python 3, no 2 (ver discusión en los comentarios).
fuente
a = [1, 2, 3, 4, 5, 6, 7, 8]f = filter(lambda x: x % 2 == 0, a)lc = [i for i in a if i % 2 == 0]>>> type(f)<class 'filter'>>>> type(lc)<class 'list'>list()en el resultado:list(filter(my_func, my_iterable)). Y, por supuesto, podría reemplazarlistconset, otuple, o cualquier otra cosa que requiera un iterable. Pero para cualquiera que no sean programadores funcionales, el caso es aún más fuerte para usar una comprensión de la lista en lugar defilteruna conversión más explícitalist.Encuentro la segunda forma más legible. Te dice exactamente cuál es la intención: filtrar la lista.
PD: no use 'lista' como nombre de variable
fuente
generalmente
filteres un poco más rápido si se usa una función integrada.Esperaría que la comprensión de la lista sea un poco más rápida en su caso
fuente
El filtro es solo eso. Filtra los elementos de una lista. Puede ver que la definición menciona lo mismo (en el enlace de documentos oficiales que mencioné antes). Mientras que la comprensión de la lista es algo que produce una nueva lista después de actuar sobre algo de la lista anterior. , digamos, un tipo de datos completamente nuevo. Como convertir enteros a cadenas, etc.)
En su ejemplo, es mejor usar filtro que comprensión de listas, según la definición. Sin embargo, si lo desea, diga other_attribute de los elementos de la lista, en su ejemplo se debe recuperar como una nueva lista, entonces puede usar la comprensión de la lista.
Así es como realmente recuerdo acerca de la comprensión de filtros y listas. Elimine algunas cosas dentro de una lista y mantenga los otros elementos intactos, use filtro. Use un poco de lógica por su cuenta en los elementos y cree una lista diluida adecuada para algún propósito, use la comprensión de la lista.
fuente
Aquí hay una pieza corta que uso cuando necesito filtrar algo después de la comprensión de la lista. Solo una combinación de filtro, lambda y listas (también conocida como la lealtad de un gato y la limpieza de un perro).
En este caso, estoy leyendo un archivo, eliminando líneas en blanco, líneas comentadas y cualquier cosa después de un comentario en una línea:
fuente
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))Además de la respuesta aceptada, hay un caso de esquina en el que debe usar filtro en lugar de una comprensión de lista. Si la lista no se puede compartir, no puede procesarla directamente con una comprensión de la lista. Un ejemplo del mundo real es si usas
pyodbcpara leer resultados de una base de datos. LosfetchAll()resultados decursores una lista inquebrantable. En esta situación, para manipular directamente los resultados devueltos, se debe utilizar el filtro:Si utiliza la comprensión de la lista aquí, obtendrá el error:
fuente
>>> hash(list()) # TypeError: unhashable type: 'list'segundo lugar, esto funciona bien:processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]Me tomó un tiempo familiarizarme con el
higher order functionsfilterymap. Así que me acostumbré a ellos y en realidad me gustó,filterya que era explícito que se filtra manteniendo lo que es verdadero y me sentí bien al saber algunosfunctional programmingtérminos.Luego leí este pasaje (Fluent Python Book):
Y ahora pienso, ¿por qué molestarse con el concepto de
filter/mapsi puede lograrlo con expresiones idiomáticas ya ampliamente difundidas como las comprensiones de listas? Ademásmapsyfiltersson un tipo de funciones. En este caso prefiero usarAnonymous functionslambdas.Finalmente, solo por el hecho de haberlo probado, he cronometrado ambos métodos (
mapylistComp) y no vi ninguna diferencia de velocidad relevante que justificara hacer argumentos al respecto.fuente
Curiosamente en Python 3, veo que el filtro funciona más rápido que las listas de comprensión.
Siempre pensé que las comprensiones de la lista serían más efectivas. Algo así como: [nombre para el nombre en brand_names_db si el nombre no es Ninguno] El código de bytes generado es un poco mejor.
Pero en realidad son más lentos:
fuente
if not Noneen la comprensión de la lista, está definiendo una función lambda (observe laMAKE_FUNCTIONdeclaración). Segundo, los resultados son diferentes, ya que la versión de comprensión de la lista eliminará solo elNonevalor, mientras que la versión del filtro eliminará todos los valores "falsos". Dicho esto, todo el propósito de la microbenchmarking es inútil. ¡Esas son un millón de iteraciones, multiplicadas por mil artículos! La diferencia es insignificante .Mi toma
fuente
inunca se dijo que fuera undict, y no hay necesidad de hacerlolimit. Aparte de eso, ¿en qué se diferencia esto de lo que sugirió el OP y cómo responde a la pregunta?