Vengo de Ruby y puedes encadenar métodos muy fácilmente. Veamos un ejemplo. Si quiero seleccionar todos los números pares de una lista y agregarle 5. Yo haría algo como esto en Ruby.
nums = [...] nums.select {|x| x % 2 == 0 }.map { |x| x + 5 }En Python eso se convierte en
nums = [...] list(map(lambda x: x + 5, filter(lambda x: x % 2 == 0, nums)))La sintaxis de Python se ve horrible. Intenté buscar en Google y realmente no encontré ninguna buena respuesta. Todo lo que vi fue cómo se puede lograr algo como esto con objetos personalizados, pero nada para procesar las listas de esta manera. ¿Me estoy perdiendo de algo?
Cuando estaba en una consola de depuración, solía ser extremadamente útil obtener algunos objetos ActiveRecord en una matriz y podía simplemente encadenar métodos para procesar las entidades para depurar cosas. Con Python, casi parece demasiado trabajo.
En Ruby, cada objeto enumerable incluye la interfaz Enumerable , por lo que tenemos todos esos métodos útiles como los que mencionas. Pero en Python, no existe una superclase común para los iterables. Un iterable se define literalmente como "una cosa que admite __iter__ ", y aunque hay una clase abstracta llamada Iterable que pretende ser una superclase de todos los iterables, en realidad no proporciona ningún método y no se encuentra en la herencia. cadena de todos los iterables (anula el comportamiento de isinstance y issubclass usando la magia de los métodos dunder, de la misma manera que puede anular + escribiendo __add__ ).
La biblioteca Alakazam implementa exactamente esta función. ( Divulgación: soy el creador y mantenedor de esta biblioteca, pero hace exactamente lo que estás pidiendo, así que lo mencionaré aquí)
Alakazam proporciona la clase Alakazam , que envuelve cualquier iterable de Python y proporciona, como métodos, todos los métodos de secuencia de Python incorporados, todo el módulo itertools y algunos otros métodos útiles orientados a la transmisión que no están incluidos en Python de forma predeterminada. . Considere su ejemplo de arriba
nums.select {|x| x % 2 == 0 }.map { |x| x + 5 }En Python, eso parece
list(map(lambda x: x + 5, filter(lambda x: x % 2 == 0, nums)))Con Alakazam, eso parece
zz.of(nums).filter(lambda x: x % 2 == 0).map(lambda x: x + 5).list()o, usando la sintaxis lambda de Alakazam
zz.of(nums).filter(_1 % 2 == 0).map(_1 + 5).list() Siempre que sea razonable, los métodos de Alakazam como el filter y el map son lentos para coincidir con el comportamiento de Python, por lo que aún debemos escribir list() al final para consumir el iterable y producir un resultado de lista único.
Como se señaló en los comentarios, este código de Ruby:
nums = [...] nums.select {|x| x % 2 == 0 }.map { |x| x + 5 } Nota: ¿por qué no usar #even? ?
nums = [...] nums.select {|x| x.even? }.map { |x| x + 5 }O incluso:
nums = [...] nums.select(&:even?).map { |x| x + 5 }Pero dejando de lado los detalles, esto se puede expresar en Python usando una lista de comprensión, que es muy limpia.
nums = [...] [x + 5 for x in nums if x % 2 == 0] Ahora, la comprensión de una lista genera ansiosamente una lista completa. Imagina una lista original como [1, 2, 3, 4, 5, 6, 7, 8] . La lista por comprensión nos daría [2, 4, 6, 8] . El conjunto de datos es trivial.
Pero imagina que nums es list(range(100_000_000)) . No es un conjunto de datos trivial. Aplicar esta comprensión de lista a todo llevará mucho tiempo, incluso si solo necesitamos los primeros cuatro valores.
Pero una expresión generadora nos permite generar perezosamente los valores que necesitamos.
from itertools import islice nums = range(100_000_000) evens_plus_five = (x + 5 for x in nums if x % 2 == 0) list(islice(evens_plus_five, 0, 5, 1)) Como se sugiere en los comentarios, esta ventaja de evaluación perezosa en grandes conjuntos de datos se puede obtener en Ruby con bastante facilidad usando #lazy y rangos.
nums = (1..100_000_000) nums.lazy.select(&:even?).map { |x| x + 5 }.take(5).to_aY si está usando Ruby 3, hagamos que ese bloque sea aún más limpio.
nums = (1..100_000_000) nums.lazy.select(&:even?).map { _1 + 5 }.take(5).to_a