Bien vu maitre Zavonen.
- W
Version imprimable
Bien vu maitre Zavonen.
- W
Parce que ça prend moins de lignes et que c’est quasi identique:Citation:
Expliquez moi pourquoi, juste pour utiliser une 'comprehension list' on construit un objet:
[[s[0]],[[s[0],s[1]], ......,]
s.append(u) produit un objet None , si bien que
[ s.append(u) for u in L if u not in s ]
produit une liste
[ None, None, None, ....]
et non pas
[[s[0]],[[s[0],s[1]], ......,]
Comme il y a s.append(u) dans les deux fonctions, cela revient presqu’au même.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 liste =[[1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48], [1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48], [1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48]] def dedoublonneCL1_devoilee(L): s = [] print [ s.append(u) for u in L if u not in s ] return s def dedoublonneCL2_devoilee(L): s = [] for u in L: if u not in s: print s.append(u) return s print dedoublonneCL1_devoilee(liste) print print dedoublonneCL2_devoilee(liste)
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 [None, None, None, None, None, None, None, None, None, None, None] [[1, 2], [158, 48], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [4, 9, 0, 1000], [0, 10], [11, 2], [2014, 2012, 2011], [138, 48]] None None None None None None None None None None None [[1, 2], [158, 48], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [4, 9, 0, 1000], [0, 10], [11, 2], [2014, 2012, 2011], [138, 48]]
La seule petite différence c’est que
dedoublonneCL2(L) laisse s’évaporer chaque None produit pas s.append(u)
tandis que dedoublonneCL1(L) les agrège en une liste avant de laisser s’évaporer elle aussi cette dernière une fois qu’elle est terminée.
Par évaporer, je veux dire qu’aucune étiquette (une variable, ayant un identifiant, qui repère l’adresse d’un objet) n’étant attachée à un objet, celui-ci n’existe donc plus, ou pas, aux yeux du comptage par référence.
Quant à la différence de temps d’exécution, elle est minime:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 from time import clock liste =[[1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48], [1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48], [1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48]] def dedoublonneCL1(L): s = [] [ s.append(u) for u in L if u not in s ] return s def dedoublonneCL2(L): s = [] for u in L: if u not in s: s.append(u) return s A = [] for fois in xrange(20): te = clock() for i in xrange(10000): x = dedoublonneCL1(liste) A.append(clock()-te) print min(A) B = [] for fois in xrange(20): te = clock() for i in xrange(10000): x = dedoublonneCL2(liste) B.append(clock()-te) print min(B)
Code:
1
2 1.97978877204 1.94248123714
Je n'ai pas parlé d'une plus grande rapidité, seulement d'une monopolisation de la mémoire moins importante.Citation:
Quant à la différence de temps d’exécution, elle est minime:
Citation:
Envoyé par auto-citation
:mouarf:
Soit on fait du code propre et on évite de construire des objets juste pour le garbage collector, soit on veut mettre le maximum de code dans un minimum de lignes et... on s'autorise (ou pas) certaines licences par rapport à code "propre".
- W
C’est vrai que sur ce plan de l’occupation mémoire, l’écueil est incontestable.Citation:
Je n'ai pas parlé d'une plus grande rapidité, seulement d'une monopolisation de la mémoire moins importante.
Mais c’est une préoccupation de programmeur en langage C, non ?
Il est indéniable que pour une liste très très très longue liste, ça peut faire une différence en occupation de mémoire.
Le tout serait de savoir à partir de quelle taille de la liste il peut y avoir atteinte des limites, relativement aux capacités d’un ordinateur donné. Avec les capacités actuelles, ça ne doit pas souvent poser problème, eu égard aux besoins de programme: vouloir dédoublonner une liste de 2 Go, ça ne doit pas être tous les jours.
Mais enfin la question existe.
Une chose dont je me suis aperçu aussi, c’est que les éléments de [ s.append(u) for u in L if u not in s ] sont tous référencés à la même adresse;
de même que les None produits dans dedoublonneCL2_devoilee() , si on les accumule dans une liste.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 liste =[[1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48], [1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48], [1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20], [1, 2], [1, 2], [4, 9, 0, 1000], [23, 45], [4, 9, 20], [1, 2], [158, 48], [345, 89, 4], [1, 2], [4, 9, 20], [0, 10], [11, 2], [345, 89, 4], [1, 2], [2014, 2012, 2011], [1, 2], [23, 45], [1, 2], [11, 2], [345, 89, 4], [138, 48]] def dedoublonneCL1_devoilee(L): s = [] sigma = [ s.append(u) for u in L if u not in s ] return s,sigma def dedoublonneCL2_devoilee(L): s,sigma = [],[] for u in L: if u not in s: sigma.append( s.append(u) ) return s,sigma s1,sig1 = dedoublonneCL1_devoilee(liste) print s2,sig2 = dedoublonneCL2_devoilee(liste) print map(id,sig1) print print map(id,sig2)
Code:
1
2
3
4 [505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884] [505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884, 505323884]
La question que je me pose alors:
quelle est l’implémentation de [ s.append(u) for u in L if u not in s ] ?
est-ce que par le jeu des caractéristiques de l’implémentation des listes en C comme le fait Python, la liste évanescente ci-dessus tient vraiment beaucoup de place , ou très peu ?
Mais je ne connais pas C et pas comment Python implémente les listes avec C pour aller plus loin.
Personnellement , si une liste doit être dédoublonnée dans le cours d’un programme, sans utiliser de fonction, j’aurai tendance à mettre la version courte en une ligne. Enfin, en 2.
Sinon, en définissant spécialement la fonction dedoublonne() , appelée pour chaque dédoublonnage, j’aurai tendance à écrire Zavonen-like, ça ne coûte pas cher et c’est indéniablement plus sûr.
None est un singleton; toute référence à None pointe vers le même objet. Pour cette raison, on peut d'ailleurs écrire x is None pour tester l'égalité avec None au lieu x == None, la première formulation étant potentiellement plus rapide (is revient à une simple égalité de pointeurs en C).
Comme None est un singleton, il n'y a pas de création d'objet, le seul espace mémoire utilisé est donc le stockage de la liste qui contient les références à None. Au minimum (en 32 bits), ça fait 4 octets par élément, mais c'est sans doute plus. Ce n'est pas énorme mais c'est aussi complètement inutile.Citation:
La question que je me pose alors:
quelle est l’implémentation de [ s.append(u) for u in L if u not in s ] ?
est-ce que par le jeu des caractéristiques de l’implémentation des listes en C comme le fait Python, la liste évanescente ci-dessus tient vraiment beaucoup de place , ou très peu ?
Mais personnellement c'est le fait d'utiliser une compréhension de liste (ou une expression génératrice) avec des effets de bord que je n'aime pas trop. Pour moi ces constructions sont plus à leur place dans un contexte fonctionnel (transparence référentielle).
Merci dividee.
Confirmation trouvée ici:Citation:
None est un singleton; toute référence à None pointe vers le même objet.
http://docs.python.org/c-api/none.html
À vrai dire, je m’en doutais. C’est parce que je pensais que tous les None produits par les s.append(u) avaient la même adresse que j’ai regardé leurs id() , pas le contraire en fait; je me suis simplement aperçu que j’avais raison.
En effet, d’après ce que j’ai compris du “data model“ de Python, il ne peut pas arriver que deux objets simples ( les objets non simples étant notamment les objets composites, ceux aussi dont la création nécessite une définition un peu élaborée ( une fonction, une classe...) , etc ) aient la même valeur (donc même type) en ayant des adresses différentes.
Or None est un objet simple.
Je n’avais jamais bien compris l’opérateur is jusqu’à présent. Maintenant c’est parfaitement clair pour moi:Citation:
Pour cette raison, on peut d'ailleurs écrire x is None pour tester l'égalité avec None au lieu x == None, la première formulation étant potentiellement plus rapide (is revient à une simple égalité de pointeurs en C).
a is b vérifie l’égalité des deux adresses contenues dans les deux variables a et b (car ce sont des variables), c’est à dire si id(a) et id(b) sont identiques,
tandis que a==b vérifie l’égalité des valeurs des deux objets pointés par les références a et b:
is teste l’égalité des valeurs des références
== teste l’égalité des valeurs des objets
Étant donné ce que j’ai écrit plus haut sur le “data model“, ces deux résultats sont toujours égaux dans le cas d’objets simples.
C’est dans le cas d’objets non simples que ces deux résultats peuvent être différents.
Citation:
The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. x is not y yields the inverse truth value.
http://docs.python.org/reference/exp...html#index-986
The operators <, >, ==, >=, <=, and != compare the values of two objects.
http://docs.python.org/reference/exp...html#index-984
Citation:
Citation:
Comme None est un singleton, il n'y a pas de création d'objet, le seul espace mémoire utilisé est donc le stockage de la liste qui contient les références à None. Au minimum (en 32 bits), ça fait 4 octets par élément, mais c'est sans doute plus. Ce n'est pas énorme mais c'est aussi complètement inutile.Citation:
La question que je me pose alors:
quelle est l’implémentation de [ s.append(u) for u in L if u not in s ] ?
est-ce que par le jeu des caractéristiques de l’implémentation des listes en C comme le fait Python, la liste évanescente ci-dessus tient vraiment beaucoup de place , ou très peu ?
C’est la conclusion que je n’osais pas avoir, n’étant pas encore très sûr qu’une valeur ne puisse pas être représentée par deux objets d’adresses différentes en Python.
Mais None étant un singleton, on n’a pas besoin d’attendre d’en être sûr pour aboutir à la conclusion qu’une liste de None construite par [ s.append(u) for u in L if u not in s ] tient peu de place en mémoire, moins en tous cas que, potentiellement, une liste [[s[0]],[[s[0],s[1]], ......,] envisagée à un moment.
Alors c’est vrai que dans une gestion des processus d’une absolue roideur, on ne devrait pas laisser à la liste [ None, None, None, None, ... ] la possibilité d’exister, même de façon évanescente ( = sans obtenir à aucun moment un identifiant) et donc coder selon Zavonen.
Cependant, vu le peu de place et surtout le temps que tient cette liste dans le déroulement d’un programme, alors que bien d’autre erreurs de conception algorithmique peuvent exister de façon moins éphémères, elles,
je suis d’avis que la formulation [ s.append(u) for u in L if u not in s ] ne mérite pas l’opprobre car elle possède un avantage qui n’est pas seulement celui de faire moins de lignes, comme je l’ai écrit précédemment.
À y réfléchir, son avantage est en effet d’être non seulement plus lisible, mais même plus “parlante“.
Quand je tombe dans un code sur
[ s.append(u) for u in L if u not in s ]
je connais tout de suite l’objectif de l’instruction:
réitérer (puisque on est dans une comprehension list) l’addition à une liste s d’un élément u, dans un processus qui est explicité dans la fin de l’instruction.
Si je veux m’intéresser au processus, je lis la fin.
Si le processus ne m’intéresse pas, j’en reste sur l’idée: « bon, là, j’additionne des éléments à s »
Tandis qu’avec :
for u in L:
« bon, j’itère dans L. Pour faire quoi ? »
....if u not in s
....« ah, on ne fait quelque chose que si u n’est pas déjà dans s
........s.append(u)
........« ah ben oui, évidemment, si u n’est pas déjà dans s, on l’y met »
Et donc, je suis obligé de passer au travers du processus avant de connaître l’objectif.
Disons que je préfère le style parlant
« construire s par addition de u pris dans L quand u n’est pas déjà dans s»
au style recette
« prendre successivement les u qui sont dans L; examiner si chaque u est déjà dans s; si c’est non alors additionner u à s »
Le coût d’une telle préférence est la constitution évanescente d’une liste de None. Qui n’est donc pas pour moi complétement inutile.
J’ai l’impression qu’il y a en informatique des présomptions de culpabilité automatiques sur certaines notions. Il suffit que sortent des mots tels que variable globale, effet de bord, MySQL, langage de script, Google, argument par défaut mutable, NoSQL, Windows, ..... pour que se mette à sonner une petite cloche qui dit : c’est mal, c’est mal...Citation:
Mais personnellement c'est le fait d'utiliser une compréhension de liste (ou une expression génératrice) avec des effets de bord que je n'aime pas trop.
Je ne suis pas très compétent en informatique, j’ose quand même dire que je ne vois pas où est l'effet de bord dans [ s.append(u) for u in L if u not in s ]
En effet
or il n’y a pas de fonction en l’occurence, il n’y a qu’une comprehension list.Citation:
En informatique, une fonction est dite à effet de bord si elle modifie un état autre que sa valeur de retour.
http://fr.wikipedia.org/wiki/Effet_d..._(informatique)
Mais surtout, comme ne sont impliqués dans l’instruction que L et s , que L n’est pas modifiée par l’instruction, que s l’est mais que c’est ce qu’on veut: où se trouve l’effet de bord ? Dans le fait que u garde une valeur après l’instruction ?
Et même s’il y a un effet de bord, quel est le problème dans la mesure où on obtient ce qu’on veut ?
Là je suis perdu. Si tu désignes par «ces constructions» les expressions ayant des effets de bord, comme une CL ou un générateur que tu évoques dans la phrase précédente, je trouve l’idée inverse dans wikipedia:Citation:
Pour moi ces constructions sont plus à leur place dans un contexte fonctionnel (transparence référentielle).
Ou bien une CL et un générateur seraient ils des monades ... ??Citation:
La programmation fonctionnelle cherche à minimiser les effets de bord et les isole souvent dans des structures prévues notamment pour cela : les monades.
http://fr.wikipedia.org/wiki/Effet_d..._(informatique)
Je nage.
Pas d'accord eyquem!Citation:
je suis d’avis que la formulation [ s.append(u) for u in L if u not in s ] ne mérite pas l’opprobre car elle possède un avantage qui n’est pas seulement celui de faire moins de lignes, comme je l’ai écrit précédemment.
À y réfléchir, son avantage est en effet d’être non seulement plus lisible, mais même plus “parlante“.
C'est peut-être une discussion byzantine, en tous cas:
[Truc for x in Machin] retourne une liste. Dans notre cas de figure [None, None, ...] dont nous n'avons rien à faire. Nous profitons dans ce style de programmation, comme le souligne Dividee de l'effet de bord qui met à jour la variable s que nous voulons récupérer à la fin.
L'argument que cela tient en une ligne est un mauvais argument.
Une grande expertise des langages permet d'écrire des codes de plus en plus courts et de plus en plus abscons. Si on me somme de le faire en 3 lignes au lieu de 6 j'écrirai par exemple:
qui reste lisible, mais je suis sûr que certains experts peuvent faire mieux (Wiztricks, Dividee sont déjà dans les starting-blocks).Code:
1
2
3 def dedoublonneCL2(L,s=[]): for u in L: s.append(u) if u not in s else None return s
Tout cela est-il bien intéressant?
A efficacité égale il faut toujours privilégier la clarté et la lisibilité.
C’est ce que je crains aussi. Mais je ne recule pas de m’enfoncer dans des discussions byzantines quand j’ai le sentiment de pouvoir découvrir à cette occasion des choses nouvelles.Citation:
C'est peut-être une discussion byzantine
Oui, bon, y a pas que ça qui compte. Il faut examiner un ensemble.Citation:
[None, None, ...] dont nous n'avons rien à faire.
-----------------------------------------
Citation:
Nous profitons dans ce style de programmation, comme le souligne Dividee de l'effet de bord qui met à jour la variable s que nous voulons récupérer à la fin.
1- Dur dur de trouver des explications sur ce qu’est un effet de bord sur le net.
2- J’ai quand même trouvé ceci qui est à un niveau théorique
http://www.alpheccar.org/fr/posts/show/15
et ceci qui est plus concret
http://open.btp.free.fr/?/prog/python3
3- J’avais mal lu l’article de Wikipedia sur l’effet de bord en concluant que cela concernait le comportement d’une fonction. En fait c’est toute expression en général qui susceptible d’effet de bord ou non.
Ce n’est donc pas à cause de la seule absence de fonction dans
[ s.append(u) for u in L if u not in s ]
que je continue de ne pas y voir d’effet de bord.
4- En effet, compte tenu du point 1 et de l’aspect théorique du premier article du point 2, je ne sais toujours pas comment et pour quelle cause se manifeste un effet de bord dans les langages en général.
5- En Python par contre, les exemples concrets du deuxième lien du point 2 me fournisse matière à réflexion.
5a) Ce type d’effet de bord ne peut pas se produire dans d’autres langages de programmation puisqu’il est lié au “data model“ de Python
5b) Question: est-ce que ce type d’effet de bord est le seul en Python, ou y a-t-il d’autres causes à un changement d’état non désiré résultant d’une expression en Python ?
5c)
Je trouve que ceci qui est vrai en général l’est encore plus en particulier pour l’exemple cité pour Python dans le lienCitation:
Effet de bord est un barbarisme né d'une traduction fautive de l'anglais side-effect (le mot side signifiant « côté » et non pas « bord », une traduction littérale et correcte aurait donné « effet latéral » ; cependant, en français, on utilise plus fréquemment l'expression synonyme « effet secondaire ».).
http://fr.wikipedia.org/wiki/Effet_d..._(informatique)
http://open.btp.free.fr/?/prog/python3
En effet, si le code
parvient à modifier le premier élément de li , c’est pace que chaque li est un objet composite, c’est à dire que ses propres éléments sont des portions de li dont les valeurs peuvent changer (donc la valeur de li peut changer) sans que l’adresse de li change.Code:
1
2
3
4 a=[[1,2],[2,3]] for li in a: li[0]+=1 print a # affiche [[2,2],[3,3]] car li est une liste mutable
Je trouve donc savoureux qu’on appelle effet de bord un processus qui concerne la valeur d’une portion nichée au cœur d’un objet composite !
5d) Par dessus le marché, je trouve parfaitement inapproprié de considérer qu’il y a là un effet de bord, c’est à dire, d’après ce que j’ai compris un peu partout et y compris dans le lien en question (« D’une manière générale, il faut éviter d’utiliser les effets de bord car ce fonctionnement est dangereux. ») un effet communément considéré comme négatif parce que non prévu.
Le comportement dans l’exemple ci-dessus est en effet totalement attendu puisque c’est le code qui le définit explicitement. Il n’y a aucun mystère, la transformation opérée sur li[0] est possible dans le cadre du “data model“ de Python, donc ça se fait et point barre.
C’est un peu fort de prétendre que ce qu’un code commande de faire est un effet habituellement considéré comme sournois.
Pour en revenir à nos moutons, j’attends toujours que vous vouliez bien me dire ce que vous entendez par effet de bord et pourquoi l’expression
[ s.append(u) for u in L if u not in s ]
en comporterait un.
Si tant est qu’un effet de bord serait ce que décrit l’exemple ci-dessus ( un changement de valeur d’un élément d’un objet mutable ), je ne verrais toujours pas pourquoi il y en aurait un dans
puisqu’on y fait append(un objet) et non pas un changement de valeur.Code:[ s.append(u) for u in L if u not in s ]
Pour le moment, je n’ai trouvé un exemple de réel effet de bord, un effet secondaire si on voulait être correct, que dans la file suivante:
http://www.developpez.net/forums/d18...ement-vicieux/
--------------------------------------------
Je suis d’accord.Citation:
L'argument que cela tient en une ligne est un mauvais argument.
Ce serait même idiot comme argument, s’il était présenté comme suffisant tout seul. Comme je disais, c’est un ensemble qu’il faut examiner: nombre de lignes, lisibilité, expressivité, vitesse, coût mémoire....
de ce point de vue:
Ce n’est pas gênant en soi que ce soit abscons, si le développeur s’y retrouve et que le programme va plus vite par exemple.Citation:
Une grande expertise des langages permet d'écrire des codes de plus en plus courts et de plus en plus abscons.
Mais je vois qu’on est d’accord:
Citation:
A efficacité égale il faut toujours privilégier la clarté et la lisibilité.
Ce qui fait d’ailleurs que je continuerai d’utiliser la formulation
tout en gardant dans un neurone ta remarque sur le poids éventuel que ça représente pour la mémoire dans le cas de grandes listes L.Code:[ s.append(u) for u in L if ...... ]
Pour moi, oui.Citation:
Tout cela est-il bien intéressant?
Par exemple , j’ai appris avec ton dernier exemple qu’on peut utiliser la formesur la même ligne qu’une instruction.Code:if u not in s else None
Je connaissais cette forme, que j’utilise dans les CL, mais je n’avais pas réalisé qu’on peut l’utiliser ainsi.
Par contre, ta fonction dedoublonneCL2(L,s=[]) ne donnera un résultat juste que pour la première utilisation:
Je me demande d’ailleurs si la modification d’un argument par défaut n’est pas un cas , réel lui, d’effet de bord.Citation:
attention à l’argument par défaut s=[] qui stocke les listes dédoublées produites lors d’appels successifs de dedoublonne()
http://www.developpez.net/forums/d95...e/#post5368324
Oui, 'effet de bord' est un barbarisme pour 'side-effect' et l'article de wiki que tu cites est parfait. Toutefois, les mauvaises habitudes ont été prises depuis longtemps alors on les garde.
Ce terme n'est peut être pas adéquat quand la fonction modifie une variable locale s. Maintenant il est vrai que sous la dernière forme donnée par moi elle modifie une variable statique, ce qui fait qu'elle ne fonctionne plus dès le second appel (chose que tu as remarquée avec ta perspicacité habituelle). Voilà ce que c'est que de vouloir économiser une ligne de code.
On peut alors le faire 'à la C' avec un effet de bord et une variable globale (l'horreur):
En résumé: On ne la fait pas à eyquem ;)Code:
1
2
3
4
5
6
7 def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None s=[] dedoublonne2(liste,s) print s
L'expression
fonctionne par effet de bord car elle modifie la valeur de s.Code:[ s.append(u) for u in L if u not in s ]
Cf Wikipedia:
On peut généraliser aux expressions, comme tu l'as découvert, eyquem.Citation:
En informatique, une fonction est dite à effet de bord si elle modifie un état autre que sa valeur de retour.
En fait, cela se voit tout de suite car le résultat de l'expression n'est pas utilisé. C'est déjà ça. En fait,
est "pire" car la première ligne donne l'impression que la valeur de retour est utile.Code:
1
2 g = (s.append(u) for u in L if u not in s) list(g)
Comme dans 98% des cas les compréhensions de listes sont utilisées sans effets de bords en Python, elle sont faciles à comprendre. Ici c'est plus vicieux, on se demande ce que la méthode append retourne (None, mais il faut le savoir). Ce n'est peut être pas important, mais il faut déjà se mettre en tête que ce n'est pas important :P. Et puis il faut remarquer que le s qui se trouve à la fin est modifié au fur et à mesure du parcours... Non décidément je préfère nettement la formulation avec une boucle explicite. L'une des forces de la compréhensions de liste, c'est que quand j'en vois une, je sais (à 98% donc) que le résultat est généré d'une façon classique (produit cartésien, map, filtrage) sans avoir à me préoccuper du côté "boucle" ou ordre d'exécution.
Pour supprimer les doublons d'une liste je propose ceci :
Code:
1
2 liste =dict().fromkeys(liste).keys()
Ça enlève même les doublon, tri, quadri etc.
ce truc a déjà fait transpiré pas mal de monde: dans ce recipe et vous verrez que nous ne sommes pas très originaux:(
- W
Ah oui, groupby() m’a fait beaucoup phosphorer, à un moment.
D’ailleurs je vois qu’il manque quelque chose dans ce code:
quand on dit que l’ordre ne compte pas, c’est pour justifier l’emploi de sort()
;)
Il faut
Code:
1
2
3
4
5 from itertools import groupby def dedoublonne(l): l.sort() return [x[0] for x in groupby(l)]
Imaginons ce que donnerait l’application d’une telle résignation dans le domaine de l’éducation des enfants....Citation:
Toutefois, les mauvaises habitudes ont été prises depuis longtemps alors on les garde.
Alors tant pis si on confusionne des générations d’apprentis programmeurs ?
En pharmacologie, les médicaments ont des effets secondaires.
Qu’est ce que ça aurait d’impossible d'abandonner “effet de bord“ pour “effet secondaire“ ?
---------------------------------------------------------
En fait , l’expression “effet de bord“ a son origine dans des sciences plus anciennes que l’informatique:
En physique, je définirais ça ainsi:Citation:
En mathématiques, et plus particulièrement en analyse numérique, un effet de bord est un phénomène d'instabilité numérique au bord de l'intervalle d'étude : en tentant de calculer une solution approchée à un problème (interpolation, équation différentielle, etc), il arrive qu'on parvienne à avoir une bonne approximation sauf sur le bord ; par exemple, le phénomène de Runge.
http://fr.wikipedia.org/wiki/Effet_d...%C3%A9matiques)
effet des conditions aux limites qu’impose la bordure d’un objet physique sur le champ modélisant un phénomène affectant cet objet; par exemple effet de bord en thermique (progression longitudinale de chaleur dans un objet), en électrostatique ( champ électrostatique dans condensateurs, ou aux abords d’une pointe) : les champs sont uniformes loin des bords mais ne le sont plus à l’approche des bords.
Plus récents, les effets de bords qu’on peut appliquer à la bordure d’une image , en traitement graphique.
Dans ces trois domaines, l’effet de bord désigne
Citation:
la modification d'une propriété lors de l'approche (au propre ou au figuré) d'une valeur.
http://fr.wikipedia.org/wiki/Effet_de_bord
On peut vaguement retrouver dans la notion d’effet de bord usitée en informatique des éléments de cette définition: certaines variables qui touchent de trop près à une fonction sont modifiées par une exécution de cette fonction.
Mais ce vague et cette définition sont insuffisants pour comprendre ce que le terme "effet de bord" désigne précisément en informatique.
-----------------------------------------------------------------
Or il est plutôt hard d'en trouver une description concrète pertinente.
Au niveau théorique, on nous dit qu’un effet de bord est
L'article est très bon, mais manque d'exemple pratiques.Citation:
la modification de l’état global de l’ordinateur (variable globales, contenu du heap ...) comme conséquence de l’exécution d’une fonction
http://www.alpheccar.org/fr/posts/show/15
Quant à
elle se limite à décrire un effet de bord valable pour les fonctions, mais il n’y a pas que les fonctions qui soient susceptibles de produire un effet de bord.Citation:
une fonction est dite à effet de bord si elle modifie un état autre que sa valeur de retour.
http://fr.wikipedia.org/wiki/Effet_d..._(informatique)
<Soit dit en passant>
dividee, tu cites cette définition à l’appui de l’affirmation
mais il n'y a pas de fonction dans cette expression !Citation:
L'expression
[ s.append(u) for u in L if u not in s ]
fonctionne par effet de bord car elle modifie la valeur de s.
Ça ne peut donc pas constituer un argument pour décrier cette expression.
D'après moi, il n’y a d'ailleurs aucun effet de bord du tout dans cette expression.
</Soit dit en passant>
De fait, c'est encore Wikipedia qui donne une meilleure, car plus générale, définition de l'effet de bord.
Elle me satisfait en tous cas beaucoup plus car elle me donne l’impression de comprendre enfin de quoi il est question quand on parle d’effet de bord en informatiqueCitation:
Il s'agit de la modification de valeurs du programme autres que celles explicitement spécifiées lors de l'appel d'une fonction ou un sous-programme.
http://fr.wikipedia.org/wiki/Effet_de_bord
Dans la page Wikipedia précédemment référée, il y avait aussi une allusion au caractère non explicite d’un effet de bord
Citation:
Dans la conception des processeurs, les instructions à effet de bord sont celles qui modifient l'état interne du processeur sans le déclarer explicitement. Par exemple une instruction d'addition peut ou non modifier des variables de conditions (retenue, zéro, débordement...). Cela peut poser un problème lors de la conception d'un processeur s'il comporte un pipeline d'instructions et des instructions à effet de bord.
http://fr.wikipedia.org/wiki/Effet_d..._(informatique)
Avec cette définition, je comprends dès lors mon incompréhension (!) à voir qualifier d’effet de bord ce qui se passe dans
[ s.append(u) for u in L if u not in s ]
L’objet s est bien explicitement engagé dans cette expression, donc il n'y a pas d'effet de bord.
Peu importe que s ne soit pas la valeur de retour de cette expression, dont on nous dit d’ailleurs qu’on n’en a rien à faire.
On ne peut pas prétendre que le changement de valeur de s est frelaté par un effet de bord du seul fait que s n’est pas la valeur de retour de la CL employée, donc prioriser une valeur de retour comme étant de plus grand intérêt dans l'absolu,
et dire en même temps que la “valeur de retour“ de la CL [None, None, None....] ne nous intéresse pas, parce que sa valeur n’a pas d’intérêt.
Ceci montre que c’est l’intérêt relatif d’une information qui conditionne l'attachement qu'on lui porte, et non pas la façon dont cette information est produite.
Donc valeur de retour ou pas, je ne vois pas ce qu’on en a à faire.
Par contre changement explicitement prévu ou pas, voilà qui fait clairement le partage entre effet de parasitage ou pas.
Bon je ne vais pas troller ad infinitum, ça commence à être plus clair pour moi et je vais en rester sur l'idée suivante:
L’effet de bord en informatique est un “parasitage“ sur certaines variables qui leur occasionne des modifications de leurs valeurs alors qu’il n’est pas explicitement spécifié qu’elles doivent les subir, de façon analogue à un post de radio qui reçoit des ondes parasites qu’il ne devrait pas capter.
Revoici un lien vers un cas patent d’un effet de parasitage qui répond à cette définition:
http://www.developpez.net/forums/d18...ement-vicieux/
=======================================
J’ai l’impression qu’il faut comprendre cette remarque comme suit : sans effet de bord = sans modification de valeur d’un objet composite.Citation:
Comme dans 98% des cas les compréhensions de listes sont utilisées sans effets de bords en Python, elle sont faciles à comprendre.
Ce qui est une vision biaisée d’après moi.
Ben oui, c'est le genre de choses qui s'apprennent une fois et qu'on sait ensuite. C'est ça, apprendre un langage , non ?Citation:
on se demande ce que la méthode append retourne (None, mais il faut le savoir). Ce n'est peut être pas important, mais il faut déjà se mettre en tête que ce n'est pas important .
Ben....oui ! C'est le principe même de l'élimination des doublons. Comment faire sans, quelle que soit la place où on le met dans le script ?Citation:
Et puis il faut remarquer que le s qui se trouve à la fin est modifié au fur et à mesure du parcours...
=======================================
Citation:
On peut alors le faire 'à la C' avec un effet de bord et une variable globale (l'horreur):
Code:
1
2
3
4
5
6 def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None s=[] dedoublonne2(liste,s) print s
L’horreur n’est pas que ce soit une variable globale, puisque ça peut se produire sans que c’en soit une, comme le montre le code suivant:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 def dedoublonne_un(L,sun): print 'id(sun) dedans A =',id(sun) print "dans dedoublonne_un(L,sun) , 'sun' in globals est ",'sun' in globals() for u in L: sun.append(u) if u not in sun else None print 'id(sun) dedans B =',id(sun) print "dans dedoublonne_un(L,sun) , 'sun' in globals est ",'sun' in globals() lettres = [['d','g'],['e','a'],['w','q'],['e','a'],['s','t'],['d','g'],['e','a'], ['w','q'],['s','t'],['w','q']] sun = [] print 'id(sun) avant =',id(sun) print "avant dedoublonne_un(L,sun) , 'sun' in globals est ",'sun' in globals() dedoublonne_un(lettres,sun) print 'id(sun) apres =',id(sun) print "apres dedoublonne_un(L,sun) , 'sun' in globals est ",'sun' in globals() print '\n-------------------------------------------\n' def chapeau(X): liste =[[1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, X], [1, 2], [1, 2], [23, 45], [4, 9, 20], [1, 2], [158, 48]] def dedoublonne_deux(L,sdeux): print 'id(sdeux) dedans A =',id(sdeux) print "dans dedoublonne_un(L,sdeux) , 'sdeux' in globals est ",'sdeux' in globals() for u in L: sdeux.append(u) if u not in sdeux else None print 'id(sdeux) dedans B =',id(sdeux) print "dans dedoublonne_un(L,sdeux) , 'sdeux' in globals est ",'sdeux' in globals() sdeux = [] print 'id(sdeux) avant =',id(sdeux) print "avant dedoublonne_un(L,sdeux) , 'sdeux' in globals est ",'sdeux' in globals() dedoublonne_deux(liste,sdeux) print 'id(sdeux) apres =',id(sdeux) print "apres dedoublonne_un(L,sdeux) , 'sdeux' in globals est ",'sdeux' in globals() a = 20 chapeau(a)
Cela montre aussi que sun à l’extérieur et à l’intérieur de la fonction dedoublonne(L,sun) est la référence d’un seul et même objet puisque l’adresse est la même,Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 id(sun) avant = 17541960 avant dedoublonne_un(L,sun) , 'sun' in globals est True id(sun) dedans A = 17541960 dans dedoublonne_un(L,sun) , 'sun' in globals est True id(sun) dedans B = 17541960 dans dedoublonne_un(L,sun) , 'sun' in globals est True id(sun) apres = 17541960 apres dedoublonne_un(L,sun) , 'sun' in globals est True ------------------------------------------- id(sdeux) avant = 17542720 avant dedoublonne_un(L,sdeux) , 'sdeux' in globals est False id(sdeux) dedans A = 17542720 dans dedoublonne_un(L,sdeux) , 'sdeux' in globals est False id(sdeux) dedans B = 17542720 dans dedoublonne_un(L,sdeux) , 'sdeux' in globals est False id(sdeux) apres = 17542720 apres dedoublonne_un(L,sdeux) , 'sdeux' in globals est False
et que ceci est vrai aussi pour sdeux à l’extérieur et à l’intérieur de la fonction dedoublonne(L,sdeux).
L'horreur est, pour moi, que sur la seule base de la lecture du code
je n’aurais pas pu prévoir le résultat, parce que j’avais dans l’esprit la notion répandue suivante:Code:
1
2
3
4
5
6 def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None s=[] dedoublonne2(liste,s) print s
EDITÉCitation:
il vaut mieux passer tout ce qu'il faut en argument : ainsi, la fonction devient une boîte noire autonome, indépendante du contexte où elle est utilisée.
C’est à dire que je pensais que l’objet référencé par le paramètre s situé dans l’intitulé de la fonction dedoublonne2(L,s) n’avait pas de retentissement sur l’objet s défini en dehors de la fonction,
en m'appuyant sur les prémisses suivantes:
- le s extérieur étant défini à l’extérieur de la fonction, il appartient à l’espace de noms extérieur à la fonction,
- le s en paramètre appartient à l’espace de noms de la fonction
- plus ou moins subconsciemment: l’objet s utilisé dans une fonction est un dupliquat du s extérieur constitué en tant qu’objet distinct du s extérieur, dont la valeur a passé au travers du paramètre s
En clair, en voyant ce code, j'aurais pensé que print s devait afficher [ ] .
Or je m’aperçois avec une double horreur qu’il n’en est rien et que tout ça était encore une grosse bouillie dans ma tête.
La première horreur, c’est que ce n’est pas vrai quand s est utilisé à la fois à l’extérieur, dans l’intitulé et à l’intérieur d’une fonction.
Il faut se rendre à l’évidence, le s intérieur est le même que le s extérieur:
Code:
1
2
3
4
5
6
7
8
9
10
11
12 def dedoublonne2(L,s): print id(s) for u in L: s.append(u) if u not in s else None print id(s) liste =[[1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2]] s=[] print id(s) dedoublonne2(liste,s) print id(s) print s
Dans ce cas de figure, s est un objet libre:Code:
1
2
3
4
5 17498064 17498064 17498064 17498064 [[1, 2], [158, 48], [23, 45]]
CORRECTIFCitation:
If a variable is used in a code block but not defined there, it is a free variable.
http://<font color="olive">http://do...binding</font>
Non, on ne peut pas affirmer ça comme ça.
La caractéristique d’un objet libre, c’est d’être défini à l’extérieur d’une fonction, que sa valeur est cependant utilisable en lecture à l’intérieur de la fonction, mais qu’on ne peut pas modifier l’objet par une instruction dite d’écriture située dans la fonction.
Seule une variable locale à une fonction peut subir une modification de sa valeur. ( Cette formulation n’est pas correcte puisque les objets en Python ne sont pas des variables, mais ça m’évite de devoir faire une formulation corecte alambiquée )
Un objet est local à une fonction quand il y a une expression qui le définit dans la fonction.
Or ici, s apparaît comme paramètre.
Et c’est bien là le problème car s étant à la fois à l’extérieur mais avec une valeur qui est vue de l’intérieur (puisqu’elle est modifiée par un append() ) et à l’intérieur en étant défini comme paramètre, je n’arrive pas à savoir comment il faut la considérer: ni libre, ni purement locale.
C'est bien ce qui me trouble.
EDIT:
J’ai éliminé ici des lignes dans lesquelles je mélangeais en désordre des considérations sur les objets libres et les objets locaux qui, sans être fausses une fois remises en ordre, n’apportaient rien par rapport au problème ci-dessus exprimé.
Deuxieme horreur: là où ça devient vicieux, c’est que les choses se produisent à l’identique même si un identifiant extérieur et les identifiants intérieurs (= paramètres) sont des noms différents, comme le montre ceci:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 def dedoublonne_un(L,sun): print 'id(sun) dedans A =',id(sun) for u in L: sun.append(u) if u not in sun else None print 'id(sun) dedans B =',id(sun) lettres = [['d','g'],['e','a'],['w','q'],['e','a'],['s','t'],['d','g'],['e','a'], ['w','q'],['s','t'],['w','q']] sG = [] print 'id(sG) avant =',id(sG) dedoublonne_un(lettres,sG) print 'id(sG) apres =',id(sG) print '\nsG =',sG print '\n-------------------------------------------\n' def chapeau(X): liste =[[1, 2], [158, 48], [1, 2], [1, 2], [23, 45], [1, 2], [23, 45], [345, 89, 4], [20, 13], [4, 9, X], [1, 2], [1, 2], [23, 45], [4, 9, 20], [1, 2], [158, 48]] def dedoublonne_deux(L,sdeux): print 'id(sdeux) dedans A =',id(sdeux) for u in L: sdeux.append(u) if u not in sdeux else None print 'id(sdeux) dedans B =',id(sdeux) sLoc = [] print 'id(sLoc) avant =',id(sLoc) dedoublonne_deux(liste,sLoc) print 'id(sLoc) apres =',id(sLoc) print '\nsLoc =',sLoc a = 20 chapeau(a)
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 id(sG) avant = 17542040 id(sun) dedans A = 17542040 id(sun) dedans B = 17542040 id(sG) apres = 17542040 sG = [['d', 'g'], ['e', 'a'], ['w', 'q'], ['s', 't']] ------------------------------------------- id(sLoc) avant = 17542800 id(sdeux) dedans A = 17542800 id(sdeux) dedans B = 17542800 id(sLoc) apres = 17542800 sLoc = [[1, 2], [158, 48], [23, 45], [345, 89, 4], [20, 13], [4, 9, 20]]
COMPLÉMENTS
Donc voilà encore une modification en catimini au sein d’une fonction d’un objet composite existant à l’extérieur de la fonction.
Dans le cas au dessus, le nom précis de l’objet se trouvait en paramètre de la fonction.
Dans ce cas ci, cela se produit aussi avec une fonction définie avec un paramètre qui n’est pas le nom de l’objet extérieur.
Les deux cas remettent en cause ma conception de l’étanchéité des fonctions.
Après réflexion, je vois le précédent cas comme identique, en fait,
compte tenu du fait qu’un nom de paramètre n’a aucune liasion avec les noms extérieurs.
Je veux dire qu’écrire:
c’est comme si on écrivaitCode:
1
2
3
4 a = 200 def f(a): print a+3 f(a)
C’est évident ,mais il y a des têtes dans lesquelles les choses rentrent difficilement.Code:
1
2
3
4 a = 200 def f(a_param): print a_param+3 f(a)
Donc en fait il n’y a pas à s’étonner que les deux cas produisent le même résultat, à savoir, pour résumer:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None lettres = [['d','g'],['e','a'],['w','q'],['e','a'],['s','t'],['d','g']] s = [] dedoublonne2(lettres,s) print 's =',s def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None lettres = [['d','g'],['e','a'],['w','q'],['e','a'],['s','t'],['d','g']] sTTTT = [] dedoublonne2(lettres,sTTTT) print 'sTTTT =',sTTTT
Code:
1
2 s = [['d', 'g'], ['e', 'a'], ['w', 'q'], ['s', 't']] sTTTT = [['d', 'g'], ['e', 'a'], ['w', 'q'], ['s', 't']]
Troisièmement
Ceci étant clarifié, ce qui a continué de me troubler un moment, c’est en définitive que dans le code
la modification de sTTTT puisse se produire alors que le paramètre s n’a pas le même nom et qu’il n’y a pas de return s.Code:
1
2
3
4
5
6
7 def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None lettres = [['d','g'],['e','a'],['w','q'],['e','a'],['s','t'],['d','g']] sTTTT = [] dedoublonne2(lettres,sTTTT) print 'sTTTT =',sTTTT
J’en reviens toujours à la même chose: pour faire sortir de la fonction étanche la valeur d’un objet local repéré par un paramètre, il me semble (semblait) qu’il faudrait une sortie par return.
Et bien s’il n’en est pas ainsi, c’est que mon idée était fausse et que les fonctions ne sont pas des boites noires absolument étanches !
Point barre.
Conclusion:
À force de réfléchir, j’en arrive à ceci:
Si les objets simples créés au sein d’une fonction ne perturbent pas leurs homonymes situés à l’extérieur, c’est dû à ce que sont le “data model“ et “l’execution model“ de Python et au fait que les fonctions travaillent sur la base d’un espace de noms personnel.
Par contre, un objet composite peut subir à l’intérieur d’une fonction strictement le même genre de “désagrément“ qu’à l’extérieur: sa valeur, c’est à dire la valeur de l’un de ses composants, peut être modifiée de façon plus ou moins voilée au travers d’une des multiples références qu’il peut avoir (comme n’importe quel objet en Python), tout en gardant son adresse (c'est là que joue l’organisation de “l’execution model“, je ne rentre pas dans les détails) et donc en affectant le même objet qui existe à l’extérieur aussi.
Ce désagrément, lorsqu'il n'implique pas de fonction, c’est par exemple le problème bien connu de la copie de liste:
Code:
1
2
3
4
5 li = [8,3,7,2,8,6,9,4] print li ld = li ld[2] += 500 print li
ld est un alias de li, et non pas une référence à un autre objet copie de li.Code:
1
2 [8, 3, 7, 2, 8, 6, 9, 4] [8, 3, 507, 2, 8, 6, 9, 4]
Et dans le cadre d’une fonction:
le paramètre s est simplement un alias de sTTTT , une deuxième étiquette attachée au même objet que l’objet référencé par sTTTT.Code:
1
2
3
4
5
6
7 def dedoublonne2(L,s): for u in L: s.append(u) if u not in s else None lettres = [['d','g'],['e','a'],['w','q'],['e','a'],['s','t'],['d','g']] sTTTT = [] dedoublonne2(lettres,sTTTT) print 'sTTTT =',sTTTT
Qu’un paramètre de fonction ne soit rien d’autre qu’un alias, puisque les arguments passés aux fonctions sont des références et non pas les valeurs des objets, cela n’a rien d’insolite. Cette évidence là ne m’avait manifestement pas encore frappé assez fort le crâne pour y pénétrer.
Donc le phénomène ne se produit pas seulement quand une fonction modifie
un objet composite repéré par une référence libre,
mais aussi quand une fonction modifie
un objet composite repéré par un paramètre qui agit comme alias d’une référence extérieure,
En fait , ce que j’ai compris aujourd’hui seulement , c’est que
- les références locales d’une fonction qui sont indépendantes de l’extérieur et disparaissent à la fermeture de la fonction sont seulement les références d’objets créés à l’intérieur d’une fonction, que ces objets soient des objets simples ou des objets composites.
L’étanchéité d’une fonction ne concerne que ces références là.
À la fermeture de la fonction, ces références locales disparaissent puisque l’espace de nom de la fonction est détruit. Ce qui revient à dire que les objets qu’elles référençaient, tous différents d’objets extérieurs puisqu’ils avaient été créés dans la fonction, disparaissent aussi et ne perturbent aucunement les objets extérieurs.
- Par contre les références libres et les objets libres dont l’existence est rattachée à l’extérieur ne disparaissent pas.
- Quant aux paramètres référençant des objets extérieurs, comme des alias, ils disparaissent aussi.
S’ils ont été impliqués dans des définitions internes dans la fonction, les objets nouveaux créés par ces définitions disparaissent, sans perturber les objets extérieurs à la fonction.
S’ils n’ont pas été impliqués dans une définition de nouvel objet, les objets extérieurs n’ont pas été perturbés dans leur localisation: leurs adresses sont restées les mêmes
MAIS ils peuvent avoir subi une modification en catimini de valeur, c’est à dire de la valeur d’un de leur composant.
C’est ce que tout le monde appelle , me semble-t-il “effet de bord“ et dont je conteste que ce soit un véritable effet de bord.
Finalement :
dans tous les cas, que ce soit à l’extérieur ou dans une fonction,
la modification de composant d’un objet composite = danger !
si on ne maîtrise pas suffisamment ce qui se passe en coulisses.
Ce serait bien d’arriver à trouver un nom pour ce phénomène qui n’a rien à voir avec un effet de bord, mais résulte simplement du “data model“ et de l’ “execution model“ de Python.
Je verrais bien:
modification subreptice d'un élément d'un objet composite fixe
fixe = pour souligner que l'objet composite lui-même reste à la même adresse.
.
Ah, un peu de pinaillage lexical (ou de mauvaise foi, fais ton choix, camarade)...
Personnellement, je ne trouve pas très pythonique d'utiliser une compréhension de liste pour autre chose que la construction d'une liste.
J'ai pas tellement d'arguments logiques (ou verbalisables) pour soutenir cette assertion, si ce n'est en reprenant une partie du Zen of Python.
Je trouve juste cette compréhension de liste contre-intuitive et hautement error-prone.
Vous savez, les boucles for existent aussi et marchent très bien.
Et quitte à choisir entre un code abscons sur une ligne et un code compréhensible sur plusieurs lignes, je prends le 2° choix. Je code en Python, pas en Perl, ce qui ne semble pas être le cas dans ce thread, si je recense le nombre de one-liners :
1.2.Citation:
[ s.append(u) for u in L if u not in s ]
3.Citation:
for u in L: s.append(u) if u not in s else None
(oui, oui, je me cite aussi !)Citation:
[x[0] for x in groupby(l)]
4.5.Citation:
dict().fromkeys(liste).keys()
6. le grand gagnant :Citation:
list(set(liste))
7.Citation:
dedoublonne = lambda L, s=[]: [ s for u in L if u not in s and (not s.append(u)) or 0 ][0]
8.Citation:
[ u for i,u in enumerate(liste[5]) if u not in liste[5][0:i] ]
9.Citation:
[ (res.append(u),vues.append(u)) if u not in vues else vues.append(u) for u in liste[5]]
8OCitation:
[list(y) for y in set([tuple(x) for x in blk[5]])]
En modifiant la liste, sans fonction, hop :) :
ou dans la même logique que les messages précédents en obfuscated python ;)Code:
1
2
3
4
5
6
7 occurences =['a','b','c','b','d','a','e'] for occ in occurences: if occurences.count(occ)>1: occurences.remove(occ) print occurences
Code:
1
2
3 occurences =['a','b','c','b','d','a','e'] print [occ for idx,occ in enumerate(occurences) if not(occurences.count(occ) >1 and occurences.index(occ)==idx) ]
je n'ai pas eu le courage de tout lire, ça a peut-être(sûrement ?) déjà été proposé:
ouCode:
1
2
3
4
5
6 L = ['a','b','c','b','d','a','e'] out = [L[0]] for i in L[1:]: if i not in out: out.append(i) out ==> ['a', 'b', 'c', 'd', 'e']
Code:
1
2
3
4
5 L = ['a','b','c','b','d','a','e'] out = [L[0]] for i in L[1:]:out += [i] if i not in out else [] out ==> ['a', 'b', 'c', 'd', 'e']