listas iteradores generadores generadoras generador funciones funcion creacion codigo python iterator generator

iteradores - ¿Por qué no hay una primera función incorporada(iterable) en Python?



listas en python (7)

Me pregunto si hay una razón por la que no hay una first(iterable) en las funciones integradas de Python, algo similar a any(iterable) y all(iterable) (puede estar metido en un módulo stdlib en alguna parte, pero no lo hago) t verlo en itertools ). first realizaría una evaluación del generador de cortocircuito para evitar operaciones innecesarias (y un número potencialmente infinito); es decir

def identity(item): return item def first(iterable, predicate=identity): for item in iterable: if predicate(item): return item raise ValueError(''No satisfactory value found'')

De esta forma puedes expresar cosas como:

denominators = (2, 3, 4, 5) lcd = first(i for i in itertools.count(1) if all(i % denominators == 0 for denominator in denominators))

Claramente, no se puede hacer la list(generator)[0] en ese caso, ya que el generador no termina.

O si tiene un grupo de expresiones regulares para hacer coincidir (útil cuando todas tienen la misma interfaz de grupo):

match = first(regex.match(big_text) for regex in regexes)

Se ahorra mucho procesamiento innecesario al evitar la list(generator)[0] y el cortocircuito en una coincidencia positiva.


Haskell hace uso de lo que acaba de describir, como la función take (o como la función parcial take 1 , técnicamente). Python Cookbook tiene generadores-wrappers escritos que realizan la misma funcionalidad que take , takeWhile y drop in Haskell.

Pero en cuanto a por qué eso no está incorporado, tu conjetura es tan buena como la mía.


Hay cierta ambigüedad en tu pregunta. Su definición de primer y el ejemplo de expresiones regulares implican que hay una prueba booleana. Pero el ejemplo de los denominadores tiene explícitamente una cláusula if; así que es solo una coincidencia que cada número entero sea verdadero.

Parece que la combinación de next e itertools.ifilter le dará lo que desea.

match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))


Hay un paquete Pypi llamado "primero" que hace esto:

>>> from first import first >>> first([0, None, False, [], (), 42]) 42

Así es como usaría para devolver el primer número impar, por ejemplo:

>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1) 7

Si solo desea devolver el primer elemento del iterador independientemente de si es verdadero o no, haga esto:

>>> first([0, None, False, [], (), 42], key=lambda x: True) 0

Es un paquete muy pequeño: solo contiene esta función, no tiene dependencias y funciona en Python 2 y 3. Es un archivo único, por lo que ni siquiera tiene que instalarlo para usarlo.

De hecho, aquí está casi todo el código fuente (de la versión 2.0.1, de Hynek Schlawack, publicado bajo la licencia del MIT):

def first(iterable, default=None, key=None): if key is None: for el in iterable: if el: return el else: for el in iterable: if key(el): return el return default


Hay un iterador "slice" en itertools. Emula las operaciones de sector con las que estamos familiarizados en python. Lo que estás buscando es algo similar a esto:

myList = [0,1,2,3,4,5] firstValue = myList[:1]

El equivalente usando itertools para iteradores:

from itertools import islice def MyGenFunc(): for i in range(5): yield i mygen = MyGenFunc() firstValue = islice(mygen, 0, 1) print firstValue


Hice una pregunta similar recientemente (ya se marcó como un duplicado de esta pregunta). También me preocupaba el hecho de que quisiera utilizar complementos solo para resolver el problema de encontrar el primer valor verdadero de un generador. Mi propia solución fue esta:

x = next((v for v in (f(x) for x in a) if v), False)

Para el ejemplo de encontrar la primera coincidencia de expresiones regulares (¡no el primer patrón coincidente!), Esto se vería así:

patterns = [ r''/d+'', r''/s+'', r''/w+'', r''.*'' ] text = ''abc'' firstMatch = next( (match for match in (re.match(pattern, text) for pattern in patterns) if match), False)

No evalúa el predicado dos veces (como tendrías que hacer si solo se devolviera el patrón) y no usa hacks como locales en las comprensiones.

Pero tiene dos generadores anidados donde la lógica dictaría usar solo uno. Entonces una mejor solución sería agradable.


Puede usar el desempaquetado de estrella que es compatible con Python 3.x. Debe leer este PEP: https://www.python.org/dev/peps/pep-3132/

x = [0, 1, 2, 3] first, *rest = x print(first) print(rest)

Python itera sobre la lista y asigna cada elemento a las variables en el lado izquierdo. Las variables con la estrella al frente, toman el resto como una nueva lista.


Si tiene un iterador, puede simplemente llamar a su next método. Algo como:

In [3]: (5*x for x in xrange(2,4)).next() Out[3]: 10