En Python (j'ai vérifié uniquement avec Python 3.6 mais je pense que cela devrait également être valable pour les versions précédentes):
(0, 0) == 0, 0 # results in a two element Tuple: (False, 0)
0, 0 == (0, 0) # results in a two element Tuple: (0, False)
(0, 0) == (0, 0) # results in a boolean True
Mais:
a = 0, 0
b = (0, 0)
a == b # results in a boolean True
Pourquoi le résultat diffère-t-il entre les deux approches? L'opérateur d'égalité gère-t-il les tuples différemment?
Les deux premières expressions s’analysent en tuples:
(0, 0) == 0
(qui est False
), suivi de 0
0
, suivi de 0 == (0, 0)
(qui est toujours False
de cette façon).Les expressions sont ainsi divisées en raison de la priorité relative du séparateur de virgule par rapport à l'opérateur d'égalité: Python voit un tuple contenant deux expressions, dont l'une se trouve être un test d'égalité, au lieu d'un test d'égalité entre deux tuples.
Mais dans votre troisième exemple, a = 0, 0
ne peut pas être un tuple. Un tuple est un ensemble de valeurs et, contrairement à un test d'égalité, une affectation n'a aucune valeur en Python. Une cession n'est pas une expression, mais une déclaration. il n'a pas de valeur pouvant être incluse dans un tuple ou toute autre expression environnante. Si vous avez essayé quelque chose comme (a = 0), 0
afin de forcer l'interprétation en tant que tuple, vous obtiendrez une erreur de syntaxe. Cela laisse l'affectation d'un tuple à une variable - ce qui pourrait être rendu plus explicite en l'écrivant a = (0, 0)
- en tant que seule interprétation valide de a = 0, 0
.
Ce que vous voyez dans les 3 instances est une conséquence de la spécification grammar du langage et de la façon dont les jetons rencontrés dans le code source sont analysés pour générer l’arbre d’analyse.
Examiner ce code de bas niveau devrait vous aider à comprendre ce qui se passe sous le capot. Nous pouvons prendre ces instructions python, les convertir en code octet puis les décompiler en utilisant le module dis
:
Cas 1: (0, 0) == 0, 0
>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
1 0 LOAD_CONST 2 ((0, 0))
3 LOAD_CONST 0 (0)
6 COMPARE_OP 2 (==)
9 LOAD_CONST 0 (0)
12 BUILD_Tuple 2
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
(0, 0)
est d'abord comparé à 0
premier et évalué à False
. Un tuple est ensuite construit avec ce résultat et le dernier 0
, vous obtenez donc (False, 0)
.
Cas 2: 0, 0 == (0, 0)
>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
1 0 LOAD_CONST 0 (0)
3 LOAD_CONST 0 (0)
6 LOAD_CONST 2 ((0, 0))
9 COMPARE_OP 2 (==)
12 BUILD_Tuple 2
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
Un tuple est construit avec 0
comme premier élément. Pour le deuxième élément, la même vérification est effectuée comme dans le premier cas et évaluée à False
, de sorte que vous obtenez (0, False)
.
Cas 3: (0, 0) == (0, 0)
>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
1 0 LOAD_CONST 2 ((0, 0))
3 LOAD_CONST 3 ((0, 0))
6 COMPARE_OP 2 (==)
9 POP_TOP
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
Ici, comme vous le voyez, vous ne faites que comparer ces deux tuples (0, 0)
et renvoyer True
.
Une autre façon d’expliquer le problème: vous connaissez probablement les littéraux de dictionnaire.
{ "a": 1, "b": 2, "c": 3 }
et littéraux de tableau
[ "a", "b", "c" ]
et littéraux Tuple
( 1, 2, 3 )
mais ce que vous ne réalisez pas, c'est que, contrairement aux littéraux de dictionnaire et de tableau, les parenthèses que vous voyez habituellement autour d'un littéral Tuple sont ne faisant pas partie de la syntaxe littérale. La syntaxe littérale pour les tuples est juste une séquence d'expressions séparées par des virgules:
1, 2, 3
(une "exprlist" dans le langage de la grammaire formelle pour Python ).
Maintenant, qu'attendez-vous du tableau littéral
[ 0, 0 == (0, 0) ]
évaluer à? Cela ressemble probablement beaucoup plus à cela devrait être le même que
[ 0, (0 == (0, 0)) ]
qui bien sûr est évalué à [0, False]
. De même, avec un littéral Tuple explicitement parenthèses
( 0, 0 == (0, 0) )
ce n'est pas surprenant d'obtenir (0, False)
. Mais les parenthèses sont optionnelles.
0, 0 == (0, 0)
est la même chose. Et c'est pourquoi vous obtenez (0, False)
.
Si vous vous demandez pourquoi les parenthèses entourant un littéral Tuple sont facultatives, c'est en grande partie parce qu'il serait ennuyeux de devoir rédiger des affectations de déstructuration de la manière suivante:
(a, b) = (c, d) # meh
a, b = c, d # better
Ajouter quelques parenthèses autour de l'ordre d'exécution des actions peut vous aider à mieux comprendre les résultats:
# Build two element Tuple comprising of
# (0, 0) == 0 result and 0
>>> ((0, 0) == 0), 0
(False, 0)
# Build two element Tuple comprising of
# 0 and result of (0, 0) == 0
>>> 0, (0 == (0, 0))
(0, False)
# Create two tuples with elements (0, 0)
# and compare them
>>> (0, 0) == (0, 0)
True
La virgule est utilisée pour séparer les expressions (en utilisant des parenthèses, nous pouvons forcer un comportement différent, bien sûr). Lors de l'affichage des extraits de code que vous avez répertoriés, la virgule ,
le sépare et définit les expressions à évaluer:
(0, 0) == 0 , 0
#-----------|------
expr 1 expr2
Le Tuple (0, 0)
peut également être décomposé de la même manière. La virgule sépare deux expressions des littéraux 0
.
Dans le premier, Python fait un tuple de deux choses:
(0, 0) == 0
, qui est évaluée à False
0
Dans le second cas, c'est l'inverse.
regardez cet exemple:
r = [1,0,1,0,1,1,0,0,0,1]
print(r==0,0,r,1,0)
print(r==r,0,1,0,1,0)
alors résultat:
False 0 [1, 0, 1, 0, 1, 1, 0, 0, 0, 1] 1 0
True 0 1 0 1 0
alors la comparaison se fait au premier nombre (0 et r) dans l'exemple.