Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

232
Views
¿Por qué `map` oculta un `StopIteration`?

Encontré un caso en el que el uso de map() no es equivalente a la comprensión de una lista. Ocurre cuando se usa a next como primer argumento.

Por ejemplo:

 l1 = [1, 2] l2 = ['hello', 'world'] iterators = [iter(l1), iter(l2)] # list comprehension values1 = [next(it) for it in iterators] # values1 = [1, "hello"] values2 = [next(it) for it in iterators] # values2 = [2, "world"] values3 = [next(it) for it in iterators] # raise StopIteration
 l1 = [1, 2] l2 = ['hello', 'world'] iterators = [iter(l1), iter(l2)] # map values1 = list(map(next, iterators)) # values1 = [1, "hello"] values2 = list(map(next, iterators)) # values2 = [2, "world"] values3 = list(map(next, iterators)) # values3 = [] # doesn't raise StopIteration

Cualquier otra excepción ocurre como debería. Ejemplo:

 def divide_by_zero(value: int): return value // 0 l = [1, 2, 3] values = list(map(divide_by_zero, l)) # raises ZeroDivisionError as expected values = [divide_by_zero(value) for value in l] # raises ZeroDivisionError as expected, too

Parece muy extraño. Funciona igual con Python 3.9 y Python 3.11.

Parece que map() funciona así:

 def map(func, iterator): try: while True: item = next(iterator) yield func(item) except StopIteration: pass

pero esperaba que funcionara así:

 def map(func, iterator): while True: try: item = next(iterator) except StopIteration: break yield func(item)

¿Es un error?

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Intente llamar next en el map :

 >>> >>> m = map(next, iterators) >>> next(m) 1 >>> next(m) 'hello' >>> next(m) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration

Es la list que ve StopIteration y la usa para detener la construcción de la lista a partir de lo que produce el map .

La comprensión de la lista, por otro lado, está construyendo la lista iterando sobre iterators , no un iterador particular en esa lista. Es decir, next(it) se usa para producir un valor para la lista, no para determinar si hemos llegado al final de los iterators .

over 4 years ago · Santiago Trujillo Report

0

Parece que ha encontrado uno de los raros casos en los que permitir que StopIteration aparezca como una excepción provoca un comportamiento incorrecto. No es un error, es solo una consecuencia desafortunada de la decisión de diseño de Python de usar una excepción para señalar el final de un iterador; es una trampa, como los argumentos predeterminados mutables , excepto que aparece con mucha menos frecuencia.

Como señala la respuesta de @chepner, el problema es que la list está capturando StopIteration y, por lo tanto, pensando que el map está agotado, cuando lo que realmente sucedió es que la función de next de llamada generó una excepción que desea tratar como una excepción real, es decir, una condición de falla .

Para evitar esto, en términos generales, no debe permitir que se llame a next en un contexto en el que StopIteration pueda surgir y quedar atrapado en el lugar equivocado. Si desea pasar next como una devolución de llamada, le sugiero que escriba un contenedor a next que convierta StopIteration en una excepción que no se malinterpretará como el final de algún otro iterador:

 def safe_next(it): try: return next(it) except StopIteration: raise ValueError('Unexpected end of iterator')

Ejemplo de uso:

 >>> l1 = [1, 2] >>> l2 = ['hello', 'world'] >>> iterators = [iter(l1), iter(l2)] >>> list(map(safe_next, iterators)) [1, 'hello'] >>> list(map(safe_next, iterators)) [2, 'world'] >>> list(map(safe_next, iterators)) Traceback (most recent call last): File "<stdin>", line 3, in safe_next StopIteration During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in safe_next ValueError: Unexpected end of iterator

Dicho esto, en este caso de uso probablemente deberías usar zip :

 >>> pairs = zip(l1, l2) >>> next(pairs) (1, 'hello') >>> next(pairs) (2, 'world') >>> next(pairs) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!