Iterables i iteradors ===================== Què vol dir iterar? ------------------- - Iterar és repetir una acció més d'una vegada. - La sentència :code:`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 :code:`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 :code:`for` o :code:`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 :code:`for`. .. exemples de strings, llistes, tuples, diccionaris i fitxers - 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 "", line 1, in 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 (:term:`file object`). Com funciona la sentència for? ------------------------------ .. exemple d'un for que recórrer una llista de 2 elements L'intèrpret de Python executa el :code:`for` >>> llista = [1, 2] >>> for element in llista: ... print(element) 1 2 com segueix: #. Crida la funció :func:`iter` sobre l'objecte iterable per tal d'obtenir un iterador: >>> it = iter(llista) #. Obté l'element de la primera iteració cridant la funció :func:`next` sobre l'iterador: >>> element = next(it) #. Executa el cos del :code:`for` >>> print(element) 1 #. Obté l'element de la segona iteració cridant la funció :func:`next` sobre l'iterador: >>> element = next(it) #. Executa el cos del :code:`for` >>> print(element) 2 #. Intenta obtenir l'element de la tercera iteració cridant la funció :func:`next` sobre l'iterador, però es produeix l'excepció :class:`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 "", line 1, in element = next(it) StopIteration #. En detectar l'excepció :class:`StopIteration`, acaba l'execució de la sentència :code:`for` i continua per la sentència següent. Què caracteritza un objecte iterable? ------------------------------------- - Té mètode :meth:`~object.__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ó :func:`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 "", line 1, in TypeError: 'int' object is not iterable - Es pot recórrer amb un :code:`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 "", line 1, in TypeError: 'int' object is not iterable Què caracteritza un objecte iterador? ------------------------------------- - Té mètode :meth:`~object.__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ó :func:`next`. >>> o = iter([1, 2, 3]) >>> e = next(o) >>> e 1 >>> o = [1, 2, 3] >>> e = next(o) Traceback (most recent call last): File "", line 1, in TypeError: 'list' object is not an iterator - Es pot recórrer amb un :code:`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 :meth:`~object.__iter__` d'un objecte iterador retorna el mateix objecte. >>> o = iter([1, 2, 3]) >>> o is iter(o) True Objectius --------- - Volem definir objectes iteradors de forma senzilla (:doc:`/iteradors/funcions-generadores`). - Volem incorporar noves estratègies per resoldre problemes (:doc:`/iteradors/itertools/index`). - Volem que els objectes de les classes contenidores definides pels programadors puguin ser iterables (:doc:`/iteradors/contenidors-iterables`).