Bonjour,
Entre :
&
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [0,0]
Quelle affectation est la plus optimale?
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [0]*2
Bonjour,
Entre :
&
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [0,0]
Quelle affectation est la plus optimale?
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [0]*2
Salut,
Quand vous écrivez a = machin, l'assignation/affectation de l'objet machin à a n'a aucune raison de dépendre de la nature de l'objet machin.
Par contre la différence entre [0, 0], liste sous forme littérale contenant deux zéros, et [0]*2 qui fabrique une liste semblable à partir d'une liste réduite à 1 seul zéro saute au yeux: il suffit de regarder les opérations effectuées.
L'affectation coûte la même chose, la création/construction de l'objet à assigner, c'est autre chose. Ici, s'il y a une différence, elle sera "epsilonnesque" et n'aura d'importance que si l'opération sera répétée plusieurs millions de fois.
Maintenant, vous vous rendez aussi compte que fabriquer une liste à N éléments nuls sera possible avec [0]*N alors que sans connaitre N, impossible d'écrire çà sous forme littérale.
- W
Notez que l'affectation
est très dangereuse (et souvent ce n'est pas ce qu'on veut) si A n'est pas 0 comme dans votre exemple, mais plutôt un objet, ou plus généralement un mutable (une liste par exemple). Dans ces cas là, si on ne connait pas N à l'avance on devra instancier comme ceci
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [A]*N
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [A for _ in range(N)]
suivit de:
Code : Sélectionner tout - Visualiser dans une fenêtre à part A = []
produira le même résultat que:
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ A for _ in range(N)]
car j'assigne dans les 2 cas N fois la référence au même objet A.
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ A ] * N
Et çà n'a rien à voir avec:
est identique à
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ [] for _ in range(N)]
dans le premier cas, je crée un objet liste vide [] à chaque itération alors que dans le 2nd je le recopie N fois.
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ [] ] * N
Subtil?
- W
Effectivement, j'ai écrit comme cela (avec A) pour avoir une écriture plus compact, mais ca peut prêter à confusion.
Donc pour être clair, oui, dans ce que j'ai écrit il faut bien comprendre (pour l'exemple disons que A vaut [1,2,3]):
.vs.
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ [1,2,3] for _ in range(N)]
(qui produisent des résultats différents)
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ [1,2,3] ] * N
et non pas
puis
Code : Sélectionner tout - Visualiser dans une fenêtre à part A=[1,2,3]
.vs.
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ A for _ in range(N)]
qui là en effet produisent la même chose.
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = [ A ] * N
Donc si je récapitule:
IDEM : À a & b est assignées 2 fois la référence de tt, soit:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 tt = [0,1] n =2 a = [tt for _ in range(n)] b = [tt] * n
Pour:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 [[0, 1], [0, 1]] [[0, 1], [0, 1]]
IDENTIQUE : c est construit par la référence de tt à chaque itération alors que la référence de tt est copié dans d 2 fois, soit:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 c = [[0,1] for _ in range(n)] d = [[0,1]] * n
Même procédé que pour c & d mais avec un résultat différent:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 [[0, 1], [0, 1]] [[0, 1], [0, 1]]
Soit:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 e = [[tt] for _ in range(n)] f = [[tt]] * n
Ai-je bien compris?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 [[[0, 1]], [[0, 1]]] [[[0, 1]], [[0, 1]]]
Non, je crois bien que vous ayez compris à l'envers !
Exécutez ces codes, et observer attentivement :
puis celui ci, qui donneras le meme résultat
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 tt = [0,1] n =2 a = [tt for _ in range(n)] print(a) a[0][0] = 17 print(a) print(tt) tt[1]=100 print(a) print(tt)
Finalement externalisé tt n'a pas vraiment d'importance puisque le code suivant produit le même comportement
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 tt = [0,1] n =2 a = [tt]*n print(a) a[0][0] = 17 print(a) print(tt) tt[1]=100 print(a) print(tt)
Mais ce dernier code, qui ne fait pas de modification collatérale, lui est différent, et est en général ce que l'on veut faire contrairement à tous les codes précédents
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 n =2 a = [[0,1]]*n print(a) a[0][0] = 17 print(a)
Je vous laisse observez ce qui se passe lorsque vous éxécutez tout cela.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 n =2 a = [[0,1] for _ in range(n) ] print(a) a[0][0] = 17 print(a)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 n =2 a = [[0,1] for _ in range(n) ] print(a) a[0][0] = 17 print(a)
Ok, je pense que j'ai saisi.
pour :
a est construit à chaque itération.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 n =2 a = [[0,1] for _ in range(n) ] print(a) a[0][0] = 17 print(a)
Ce qui implique qu'à la première itération à l'indice 0 de a et à l'indice 0 de l'objet contenu dans a sera assigné 17 soit [17,1]. À la seconde itération comme aucune autre instruction est donnée, l'objet est construit dans sa valeur initiale soit [0,1]. On a donc:
Pour tous les autres blocs de code l'objet contenu dans a est copié n fois.
Code : Sélectionner tout - Visualiser dans une fenêtre à part [[17, 1], [0, 1]]
Ce qui implique que si on assigne une nouvelle valeur à l'indice 0 de a et à l'indice 0 de l'objet contenu dans a on change la valeur de référence. On aura donc:
Merci beaucoup a vous deux pour cette excellente leçon
Code : Sélectionner tout - Visualiser dans une fenêtre à part [[17, 1], [17, 1]]
Ceci étant,
Pourquoi cette affectation est très dangereuse?Notez que l'affectation
Code :
a = [A]*N
est très dangereuse
Bonjour
Ce n'est pas l'affectation en elle-même qui est dangereuse mais, et vous l'avez bien compris, que le résultat dépend de la nature de "A".
Si "A" est un simple int/double ça ira mais si "A" est un truc plus complexe et surtout mutable (liste, dico) alors il est simplement référencé N fois.
Et ensuite modifier a[0] reviendra à modifier une unique référence ce qui se répercute alors dans tous les a[x].
Et donc ce code suivant
produira le résultat suivant
Code python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 a=[[0,]] * 5 print(a) a[2][0]=18 print(a)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 [[0], [0], [0], [0], [0]] [[18], [18], [18], [18], [18]]
Mon Tutoriel sur la programmation «Python»
Mon Tutoriel sur la programmation «Shell»
Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
Et on poste ses codes entre balises [code] et [/code]
Non. C'est 2 lignes là :
construisent chacune une liste.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 a=[[0,1] ] * 5 b=[[0,1] for _ in range(5) ]
Faire ca ensuite
ce n'est pas faire une 2eme itération dans la construction de votre liste. Non la liste est déjà créée. Faire ca, c'est assigner une nouvelle valeur a un élément de la liste (élément d'une sous liste pour être plus précis).
Code : Sélectionner tout - Visualiser dans une fenêtre à part a[2][0]=18
En fait non.
ici [0,1] est littéralement copié 5 fois, et vous obtenez à la fin 5 copies indépendantes
Code : Sélectionner tout - Visualiser dans une fenêtre à part b=[[0,1] for _ in range(5) ]
ici [0,1] n'est créé qu'une seul fois, et on met 5 références vers cette même liste. Ce qui explique pourquoi si vous en modifiez un, il sont tous modifié: il pointe tous sur la même liste d'origine.
Code : Sélectionner tout - Visualiser dans une fenêtre à part a=[[0,1] ]*5
Merci à vous trois pour votre patience.
D'accord... donc,
avec en test,
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 tt = [0,1] a1 = [[0,1]]*5 a2 = [tt for _ in range(5)] a3 = [tt]*5
ici [0,1] & tt sont créés 1 seule fois chacun pour 5 références vers 1 liste.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 a1[2][0]=18 a2[2][0]=18 a3[2][0]=18
Conséquence : la modification d'une seule référence entraîne la modification de toutes les autres car elles pointent toutes vers la même liste d'origine.
Cette liste d'origine étant [0,1] & tt selon l'exemple.
avec en test,
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 b1 = [[0,1] for _ in range(5)] b2 = [0,1]*5
ici [0,1] est copié 5 fois chacun pour 5 copies indépendantes dans 1 liste.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 b1[2][0]=18 b2[4]=18
Ok, mais je suis incapable de lire une différence entre les assignations des a et des b même si je suis prête à admettre ces explications. Entre a2 et b1 par exemple il n'y a rien qui me saute aux yeux et encore moins entre a1 et b2, comment faites-vous pour voir ces différences?
On s'est déjà fait avoir assez souvent pour déceler ces différences et on a appris a y mettre des mots dessus:
Ici vous voyez (ou pas) qu'on crée 5 fois une référence au même objet que celui assigné à tt.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 tt = [0,1] a2 = [tt for _ in range(5)]
Par contre ici:
La variable a été remplacé par l'expression littérale [0, 1] qui dit crée une liste avec les nombres 0 et 1 dedans. Répété 5 fois, on aura 5 objets égaux mais différents.
Code : Sélectionner tout - Visualiser dans une fenêtre à part b1 = [[0,1] for _ in range(5)]
- W
Attention à la syntaxe. Je présume que tu veux rendre b2 similaire à b1 pour tes tests. Or [0,1]*5 va copier 5 fois d'affilée la suite "0, 1" dans un simple tableau => b2=[0, 1, 0, 1, 0, 1, 0, 1, 0, 1] et ne correspondra absolument pas à b1 qui, lui, est un vrai tableau 2D.
Pour rendre b2 similaire à b1 il faut écrire b2=[[0, 1],]*5 indiquant qu'on veut créer un tableau contenant 5 fois le couple [0, 1]. Et la virgule avant le dernier crochet n'est pas obligatoire pour une liste mais l'est pour un tuple (ce n'est pas la parenthèse qui fait le tuple mais la virgule ainsi (0) est un simple int tandis que (0,) est lui un vrai tuple) donc perso je la mets tout le temps pour rester cohérent dans ma syntaxe.
Ben déjà on sait (et c'est bien ancré) que répéter un truc complexe c'est super dangereux donc quand on se lance, on fait super gaffe. Ensuite si on se trompe malgré tout, le programme part en cacahouette dès le premier test, donc on met un print() et on voit immédiatement qu'on s'est gauffrés. Et puis bon, ce n'est pas non plus une syntaxe dont on a besoin tous les 4 matins...
Mon Tutoriel sur la programmation «Python»
Mon Tutoriel sur la programmation «Shell»
Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
Et on poste ses codes entre balises [code] et [/code]
Partager