Iterables i iteradors

Què vol dir iterar?

  • Iterar és repetir una acció més d’una vegades.

  • La sentència for permet iterar.

    >>> a = [5, -2, 3]
    >>> s = 0
    >>> for e in a:        # capçalera
    ...     if e > 0:      # cos
    ...         s = s + e  # cos
    
  • La sentència while permet iterar.

    >>> m = 100
    >>> i = 0
    >>> while 2**i < m:    # capçalera
    ...     i = i + 1      # cos
    
  • L’acció que es repeteix quan iterem amb una sentència for o while és el cos de la sentència.

Què és un objecte iterable?

  • Un objecte és iterable si podem recórrer els seus elements un rere l’altre en una sentència for.

  • Les llistes són iterables:

    >>> llista = [1, 2, 3]
    >>> p = 1
    >>> for element in llista:
    ...     p = p * element
    >>> p
    6
    
  • Els tuples són iterables:

    >>> tuple = (1, 2, 3)
    >>> p = 1
    >>> for element in tuple:
    ...     p = p * element
    >>> p
    6
    
  • Els diccionaris són iterables:

    >>> dicc = {'un':1, 'dos':2, 'tres': 3}
    >>> p = 1
    >>> for clau in dicc:
    ...     p = p * dicc[clau]
    >>> p
    6
    
  • Els strings són iterables:

    >>> cadena = "123"
    >>> p = 1
    >>> for car in cadena:
    ...     p = p * int(car)
    >>> p
    6
    
  • Els fitxers són iterables:

    >>> p = 1
    >>> with open('nom_fitxer.txt', 'r') as f:
    ...     for linia in f:
    ...         p = p * int(linia)
    >>> p
    6
    
  • Els enters no són iterables:

    >>> enter = 123
    >>> p = 1
    >>> for digit in enter:
    ...     p = p * digit
    Traceback (most recent call last):
      File "/usr/lib/python3.5/doctest.py", line 1321, in __run
        compileflags, 1), test.globs)
      File "<doctest iterables.txt[24]>", line 1, in <module>
        for digit in enter:
    TypeError: 'int' object is not iterable
    

Què és un objecte iterador?

  • Un objecte és iterador si és iterable, però només podem recórrer el seus elements un sol cop.

  • Els objectes iteradors s’esgoten: no es poden tornar a recórrer.

    >>> with open('nom_fitxer.txt', 'r') as f:
    ...     print('Primer recorregut')
    ...     for linia in f:
    ...         print(linia, end='')
    ...     print('\nSegon recorregut')
    ...     for linia in f:
    ...         print(linia, end='')
    Primer recorregut
    1
    2
    3
    Segon recorregut
    
  • Per tant, podem classificar els objectes iterables com:

    Contenidors iterables

    objectes contenidors que es poden recórrer múltiples vegades. Per exemple, strings, llistes, tuples o diccionaris.

    Iteradors

    objectes que només es poden recórrer un cop. Per exemple, els fitxers (file object).

Com funciona la sentència for?

L’intèrpret de Python executa el for

>>> llista = [1, 2]
>>> for element in llista:
...     print(element)
1
2

com segueix:

  1. Crida la funció iter() sobre l’objecte iterable per tal d’obtenir un iterador:

    >>> it = iter(llista)
    
  2. Obté l’element de la primera iteració cridant la funció next() sobre l’iterador:

    >>> element = next(it)
    
  3. Executa el cos del for

    >>> print(element)
    1
    
  4. Obté l’element de la segona iteració cridant la funció next() sobre l’iterador:

    >>> element = next(it)
    
  5. Executa el cos del for

    >>> print(element)
    2
    
  6. Intenta obtenir l’element de la tercera iteració cridant la funció next() sobre l’iterador, però es produeix l’excepció StopIteration perquè l’iterable no té més elements:

    >>> element = next(it)
    Traceback (most recent call last):
      File "/usr/lib/python3.5/doctest.py", line 1321, in __run
        compileflags, 1), test.globs)
      File "<doctest iterables.rst[31]>", line 1, in <module>
        element = next(it)
    StopIteration
    
  7. En detectar l’excepció StopIteration, acaba l’execució de la sentència for i continua per la sentència següent.

Què caracteritza un objecte iterable?

  • Té mètode __iter__().

    >>> o = [1, 2, 3]
    >>> hasattr(o, '__iter__') # les llister són iterables
    True
    >>> o = 3
    >>> hasattr(o, '__iter__') # els enters no
    False
    
  • Pot ser argument de la funció iter().

    >>> o = [1, 2, 3]
    >>> it = iter(o) # les llister són iterables
    >>> o = 3
    >>> it = iter(o) # els enters no
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'int' object is not iterable
    
  • Es pot recórrer amb un for.

    >>> o = [1, 2, 3]
    >>> for e in o:
    ...     print(e)
    ...
    1
    2
    3
    >>> o = 3
    >>> for e in o:
    ...     print(e)
    ...
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'int' object is not iterable
    

Què caracteritza un objecte iterador?

  • Té mètode __next__().

    >>> o = iter([1, 2, 3]) # iter retorna un iterador
    >>> hasattr(o, '__next__')
    True
    >>> o = [1, 2, 3]
    >>> hasattr(o, '__next__')
    False
    
  • Pot ser argument de la funció next().

    >>> o = iter([1, 2, 3])
    >>> e = next(o)
    >>> e
    1
    >>> o = [1, 2, 3]
    >>> e = next(o)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'list' object is not an iterator
    
  • Es pot recórrer amb un for només un cop.

    >>> o = iter([1, 2, 3])
    >>> for e in o:
    ...     print(e)
    ...
    1
    2
    3
    >>> for e in o:
    ...     print(e)
    ...
    >>>
    
  • El mètode __iter__() d’un objecte iterador retorna el mateix objecte.

    >>> o = iter([1, 2, 3])
    >>> o is iter(o)
    True
    

Objectius