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:

Código:
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():

Código:
>>> num = [1, 2, 3, 4, 5]
>>> es = ["uno", "dos", "tres"]
>>> en = ["one", "two", "three"]
>>> mi_objeto = zip(num, es, en)
>>> next(mi_objeto)
(1, 'uno', 'one')
>>> next(mi_objeto)
(2, 'dos', 'two')
>>> next(mi_objeto)
(3, 'tres', 'three')
>>> next(mi_objeto)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
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:

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

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

Código:
>>> mi_objeto = list(zip(num, es, en))
>>> print(mi_objeto)
[(1, 'uno', 'one'), (2, 'dos', 'two'), (3, 'tres', 'three')]
>>> for x, y, z in mi_objeto:
...     print(x, y, z)
...
1 uno one
2 dos two
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
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
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)