Les list comprehensions sont est une syntaxe particulière permettant de construire facilement et rapidement des listes.
La syntaxe est les suivantes:
[ expression-manipulation for expression in sequence ]
[ expression-manipulation for expression in sequence if test]
>>> [word.upper() for word in "c est une phrase".split() ] ['C', 'EST', 'UNE', 'PHRASE'] # ou code equivalent mais plus long >>> myList = [] >>> for word in "c est une phrase".split(): ... myList.append( word.upper() ) ... >>> myList ['C', 'EST', 'UNE', 'PHRASE'] # Autre exemple de list comprehension >>> [ i * 3 for i in range(10) if i%2==0] [0, 6, 12, 18, 24]
Les itérateurs
L'iteration est un mecanisme permettant de passer en revue le contenu d'une liste en utilisant le principe d'iteration.
Un itérateur implemente la fonction next() qui retourne la prochaine valeur (ou l'exception StopIteration quand il n'y a plus d'élément).
Les itérateurs sont utilisés de façon transparente dans les syntaxes tel que "for item in sequence:".
Il est également possible d'obtenir un itérateur (implémentant la fonction next) en utilisant la primitive iter( anObjectExposingIteratorFunction ).
On appele ensuite la fonction next() de l'iterateur pour retrouver les différents élements.
L'exemple suivant est disponible dans le fichier testIterator.py .
>>> def UseIterator(): """ l exemple suivant démontre l'utilisation explicite d'un itérateur. Pour information, l'instruction for fait un usage implicite de L'itérateur """ mySequence = ['a', 'b', 5 ,7 ,9 ,'tz'] _iterator = iter( mySequence ) try: while True: _value = _iterator.next() print( _value ) except StopIteration: print( '--- End of iteration ---' ) >>> testIterator.UseIterator() a b 5 7 9 tz --- End of iteration ---
Définir son propre itérateur
Il est possible de definir un iterateur pour ses propres classes.
Dans ce cas, il est nécessaire d'implémenter la fonction __iter__ retournant une référence vers un object implémentant la methode next().
NB: la fonction __iter__ peut être utilisée pour retourner une référence vers l'objet lui-même si ce dernier implémente la fonction next(). Ce sera le cas de l'exemple ci-dessous aussi disponible dans le fichier testIterator.py .
import random def UseIterator(): mySequence = ['a', 'b', 5 ,7 ,9 ,'tz'] _iterator = iter( mySequence ) try: while True: _value = _iterator.next() print( _value ) except StopIteration: print( '--- End of iteration ---' ) class myIterableClass(object): """ class contenant une liste d'élément iterable. Attention: Ceci est un exemple pour faciliter la compréhension. Cette classe ne sais produire qu'un seul iterateur à la fois car elle implémente elle meme son propre itérateur et par conséquent l'index est reseter a chaque obtention de l'itérateur (via __iter__). La solution a ce problème est d'implémenter une classe d'iteration indépendante. """ _list = [] _index = 0 # cursor for iteration # Constructor def __init__( self ): # prepare a random list of items for i in range( 1, random.randint(1,25) ): self._list.append( 'Value %s is %s ' % (i, random.randint(1,1000) ) ) # Iterable primitive def __iter__( self ): self._index = 0; return self # Iteration function def next( self ): self._index += 1 if self._index-1 >= len( self._list ): raise StopIteration() return self._list[self._index-1] >>> # Utilisation de la classe iterable >>> # >>> obj = myIterableClass() >>> for theValue in obj: ... print( theValue ) ... Value 1 is 687 Value 2 is 3 Value 3 is 427 Value 4 is 954 >>> # Utilisation de la classe iterable >>> # via la declaration d'un iterateur >>> it = iter( obj ) >>> it.next() 'Value 1 is 687 ' >>> it.next() 'Value 2 is 3 ' >>> it.next() 'Value 3 is 427 ' >>> it.next() 'Value 4 is 954 ' >>> it.next()
Les générateurs (Yield)
Yield est un mot clé permettant de retourner un contenu énumerable.
Dans le monde Python, la syntaxe utilisant le mot clé "yield" correspond au concept de "générateur" (generator).
L'interpréteur utilise le générateur qui mémorisera l'état de la fonction appelée. Comme précisé par Tarek Ziadé, "la directive yield constituant en quelque sorte un return avec point de sauvegarde de l'état des variables locales et de l'endroit où le code de la fonction à quitté." (Source: Programmation Python seconde Edt., Page 149, ISBN 9782-212-12483-5)
L'exemple suivant issu du script testIterator.py démontre l'usage du mot clé yield.
class YieldedItem( object ): """ Classe de demonstration démontrant l'utilité du mot clé "yield". Voir les fonctions TestYielded et BuildYielded """ _value = -1 def __init__(self): self._value = random.randint(1,25) def __getValue( self ): return self._value value = property( __getValue, None, None ) def BuildYielded(): """ Cette fonction génère une série d'objects de type YieldedItem() et retourne les références à l'aide du mot clé "yield". Les objects ainsi crées seront énumerables """ for i in range( 10 ): print( 'BuildYielded(): Create YieldedItem() object #%s' % (i) ) yield YieldedItem() def TestYielded(): """ Cette fonction démontre l'utilité du mot clé "yield". En effet, la fonction BuildYielded() retourne une série d'objet à l'aide du mot clé "yield". Par conséquent, les différents objets sont stockés dans un générateur qui lui est énumérable. """ # Exemple démontrant l'usage du générateur print( 'La fonction BuildYielded() retourne un générateur.' ) print( 'Notez que la fonction BuildYielded() n\'est pas exécutée!') print( BuildYielded() ) # Exemple démontrant le caractère énumérable print( 'Le resultat de la fonction peut être facilement énuméré.' ) print( 'Notez l\'entrelas des appels entre TestYielded() et ' + 'BuildYielded()' ) for item in BuildYielded(): print( 'TestYielded(): Valeur stockée dans l\'objet #%s est %s' % (id(item),item.value) ) # Exemple stockant la référence du générateur print( ' ' ); print( 'Autre exemple en stockant la référence du générateur' ) gen = BuildYielded(); for item in gen: print( 'TestYielded(): Valeur stockée est %s' % ( item.value) )
Le code précédent produissant le résultat ci-dessous.
Notez l'entrelas des appels entre les fonctions BuildYielded() et TestYielded()! Les générateurs sont plus particulièrement approprié lors de la manipulation de grands ensembles/set d'objets (voir l'exemple du point suivant concernant les générateurs et les listes de compréhension).
>>> TestYielded() La fonction BuildYielded() retourne un générateur. Notez que la fonction BuildYielded() n'est pas exécutée! <generator object at 0x839086c> Le resultat de la fonction peut être facilement énuméré. Notez l'entrelas des appels entre TestYielded() et BuildYielded() BuildYielded(): Create YieldedItem() object #0 TestYielded(): Valeur stockée dans l'objet #137953772 est 4 BuildYielded(): Create YieldedItem() object #1 TestYielded(): Valeur stockée dans l'objet #137955468 est 6 BuildYielded(): Create YieldedItem() object #2 TestYielded(): Valeur stockée dans l'objet #137953772 est 7 BuildYielded(): Create YieldedItem() object #3 TestYielded(): Valeur stockée dans l'objet #137955468 est 9 BuildYielded(): Create YieldedItem() object #4 TestYielded(): Valeur stockée dans l'objet #137953772 est 8 BuildYielded(): Create YieldedItem() object #5 TestYielded(): Valeur stockée dans l'objet #137955468 est 20 BuildYielded(): Create YieldedItem() object #6 TestYielded(): Valeur stockée dans l'objet #137953772 est 11 BuildYielded(): Create YieldedItem() object #7 TestYielded(): Valeur stockée dans l'objet #137955468 est 18 BuildYielded(): Create YieldedItem() object #8 TestYielded(): Valeur stockée dans l'objet #137953772 est 1 BuildYielded(): Create YieldedItem() object #9 TestYielded(): Valeur stockée dans l'objet #137955468 est 20 Autre exemple en stockant la référence du générateur BuildYielded(): Create YieldedItem() object #0 TestYielded(): Valeur stockée est 3 BuildYielded(): Create YieldedItem() object #1 TestYielded(): Valeur stockée est 18 BuildYielded(): Create YieldedItem() object #2 TestYielded(): Valeur stockée est 19 BuildYielded(): Create YieldedItem() object #3 TestYielded(): Valeur stockée est 9 BuildYielded(): Create YieldedItem() object #4 TestYielded(): Valeur stockée est 15 BuildYielded(): Create YieldedItem() object #5 TestYielded(): Valeur stockée est 3 BuildYielded(): Create YieldedItem() object #6 TestYielded(): Valeur stockée est 4 BuildYielded(): Create YieldedItem() object #7 TestYielded(): Valeur stockée est 22 BuildYielded(): Create YieldedItem() object #8 TestYielded(): Valeur stockée est 6 BuildYielded(): Create YieldedItem() object #9 TestYielded(): Valeur stockée est 9Les générateurs et comprehension list
Finalement, il est également possible d'associer les générateurs et les compréhensions lists (décrit en début d'article). le résultat étant appelé un "Generator Expression"!
Pour créer une générateur sur base d'une compréhension list, l'on utilise la syntaxe suivante où les parenthèses remplacent les crochets.
aGenerator = ( expression-manipulation for expression in sequence if test)
Un cas typique de son utilisation serait l'exécution de l'expression de manipulation (expression-manupilation) que lorsque l'on extrait l'information du generateur. Cela peut devenir utile si le processus est grand consommateur de ressource (comme des accès DB).
A Contrario, toutes les expressions de manipulation sont exécutées immédiatement lors de la création d'une expression list.
Voici un exemple extrait du module GenVsExpList.py .
""" Ce module d'exemple sert a mettre en évidence la différence de fonctionnement entre un Generator et une Comprehension List """ def transformIntForGen( iValue ): """Cette fonction de transformation est appelée pour transformer les valeurs d'un générateur d'expression. Grace à la trace, il est possible de déterminer le mode de fonctionnement.""" print( '%s: pour la value %s' % ( 'transforme for Generator', iValue) ) return 100-iValue def transformIntForCompList( iValue ): """Cette fonction de transformation est appelée pour transformer les valeurs d'une comprehension list. Grace à la trace, il est possible de déterminer le mode de fonctionnement.""" print( '%s: pour la value %s' % ( 'transform ComprehensionList', iValue) ) return 100-iValue def compareGenVsCompList(): """Fonction de comparaison d'execution de generateur et comprehension list""" print( '--- Comprehension List execution ---' ) print( '1) prepare comprehension list' ) _list = [ transformIntForCompList(i) for i in range(25) if i%3==0 ] print( '2) display comprehension list content' ) for index in range( len(_list) ): print( 'Comprehension List value #%s : %s' % ( index, _list[index]) ) print( '--- Generator expression execution ---' ) print( '1) prepare generator' ) _gen = ( transformIntForGen(i) for i in range(25) if i%3==0 ) print( '2) display generator expression content' ) # Dans le cas d'un générayeur, il faut malheureusement # gérer l'index soit même. _idx = 0 for item in _gen: print( 'Generator expression value #%s : %s' % ( _idx, item) ) _idx += 1
Le résultat démontre bien que dans le cas d'un generator expression, l'expression de transformation n'est appelée qu'au moment de l'extraction de la valeur!
>>> compareGenVsCompList() --- Comprehension List execution --- 1) prepare comprehension list transform ComprehensionList: pour la value 0 transform ComprehensionList: pour la value 3 transform ComprehensionList: pour la value 6 transform ComprehensionList: pour la value 9 transform ComprehensionList: pour la value 12 transform ComprehensionList: pour la value 15 transform ComprehensionList: pour la value 18 transform ComprehensionList: pour la value 21 transform ComprehensionList: pour la value 24 2) display comprehension list content Comprehension List value #0 : 100 Comprehension List value #1 : 97 Comprehension List value #2 : 94 Comprehension List value #3 : 91 Comprehension List value #4 : 88 Comprehension List value #5 : 85 Comprehension List value #6 : 82 Comprehension List value #7 : 79 Comprehension List value #8 : 76 --- Generator expression execution --- 1) prepare generator 2) display generator expression content transforme for Generator: pour la value 0 Generator expression value #0 : 100 transforme for Generator: pour la value 3 Generator expression value #1 : 97 transforme for Generator: pour la value 6 Generator expression value #2 : 94 transforme for Generator: pour la value 9 Generator expression value #3 : 91 transforme for Generator: pour la value 12 Generator expression value #4 : 88 transforme for Generator: pour la value 15 Generator expression value #5 : 85 transforme for Generator: pour la value 18 Generator expression value #6 : 82 transforme for Generator: pour la value 21 Generator expression value #7 : 79 transforme for Generator: pour la value 24 Generator expression value #8 : 76
1 commentaire:
C'est un peu rapide tout ça, et beaucoup de notions.
Vous pourriez développer un peu chaque notion, avec plus de détails ?
Un peu comme ça:
http://sametmax.com/python-love-les-listes-en-intention-partie/
Mais pour tous les sujets.
Enregistrer un commentaire