Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Función zip(). Duda.
#1
Hola, recién empiezo a intentar aprender programación con Python y no consigo entender porqué se produce esta aparente incongruencia
relacionada con el comportamiento de la función zip():

1-Ejemplo A:
En este ejemplo, los 2 print()  funcionan sin problemas.
num = [1, 2, 3, 4, 5]
es = ["uno", "dos", "tres"]
en = ["one", "two", "three"]
mi_objeto = list(zip(num, es, en))

print(mi_objeto)

for x, y, z in mi_objeto:
  print(x, y, z)

2-Ejemplo B:
Aquí el bucle NO produce ningún resultado:

mi_objeto = zip(num, es, en)

print(list(mi_objeto))

for x, y, z in mi_objeto:
  print(x, y, z)


Si la única función de print() es mostrar en pantalla, y no ejecuta nada más,
¿Porqué el último bucle no "imprime" nada?

Muchas gracias de antemano Smile
Responder
#2
Hola, bienvenido.

Es interesante la pregunta. Lo fundamental para entender lo que ocurre es reconocer que el resultado de zip() es un generador. Los generadores son objetos que, justamente, generan el contenido a medida que es accedido. Esto quiere decir que en esta línea:

  1. mi_objeto = zip(num, es, en)


La función zip() todavía no hizo nada con las tres listas pasadas como argumento: los valores se van a generar cuando el objeto sea recorrido con un bucle for o, para ser más precisos, cuando se le solicite un valor vía la función next():

  1. >>> num = [1, 2, 3, 4, 5]
  2. >>> es = ["uno", "dos", "tres"]
  3. >>> en = ["one", "two", "three"]
  4. >>> mi_objeto = zip(num, es, en)
  5. >>> next(mi_objeto)
  6. (1, 'uno', 'one')
  7. >>> next(mi_objeto)
  8. (2, 'dos', 'two')
  9. >>> next(mi_objeto)
  10. (3, 'tres', 'three')
  11. >>> next(mi_objeto)
  12. Traceback (most recent call last):
  13. File "<stdin>", line 1, in <module>
  14. StopIteration


Cada llamada a next() le solicita a mi_objeto un elemento. Cuando ya generó y retornó todos los elementos, una sucesiva llamada a next() arroja la excepción StopIteration. El principal beneficio de que los elementos se generen a medida que se obtienen vía next() y no desde el momento de la llamada a zip() es que nunca están almacenados todos en memoria, de modo que tener un zip() de 2 elementos o de un millón es exactamente lo mismo (i. e. ocupan la misma memoria).

Si lo ponemos en términos mas simples, podemos decir esto: los objetos retornados por zip() solo pueden ser recorridos una vez ("recorrer" un elemento es llamar sucesivamente a next() hasta que lance StopIteration, que es lo que hace internamente el bucle for). Cuando convertís un zip() a una lista, la función incorporada list() internamente está recorriendo ese objeto, entonces luego ya no podés hacer un for sobre él:

  1. >>> mi_objeto = zip(num, es, en)
  2. >>> list(mi_objeto) # Esto recorre el zip.
  3. [(1, 'uno', 'one'), (2, 'dos', 'two'), (3, 'tres', 'three')]
  4. >>> for x, y, z in mi_objeto: # Aquí ya no queda nada por recorrer.
  5. ... print(x, y, z)
  6. ...


Pero en tu primer ejemplo vos te deshacés del zip y te quedás con la lista desde un primer momento:

  1. >>> mi_objeto = list(zip(num, es, en))
  2. >>> print(mi_objeto)
  3. [(1, 'uno', 'one'), (2, 'dos', 'two'), (3, 'tres', 'three')]
  4. >>> for x, y, z in mi_objeto:
  5. ... print(x, y, z)
  6. ...
  7. 1 uno one
  8. 2 dos two
  9. 3 tres three


Las listas tienen todos sus elementos almacenados en memoria, por lo cual pueden ser recorridas todas las veces que sea necesario, a expensas de que cuanto más grande es la lista, más memoria ocupa.

Espero haber sido claro.

Saludos
Responder
#3
Muchas gracias por tu respuesta, Francisco. Muy amable. Tu explicación ha sido muy completa, amena y didáctica. Ahora me quedó más claro.
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)