Ce n'est pas un raccourci.
Le +=
symbole est apparu dans le langage C dans les années 1970 et - avec l’idée C d’assembleur intelligent, correspond à une instruction machine et à un mode d’adressage nettement différents:
Des choses comme " i=i+1
", "i+=1
"et" ++i
", bien qu’à un niveau abstrait produisent le même effet, correspondent à un niveau bas à une manière différente de travailler du processeur.
En particulier ces trois expressions, en supposant que la i
variable réside dans l'adresse de la mémoire stockée dans un registre de la CPU (appelons-la D
- pensez à un "pointeur sur int") et que l' ALU du processeur prend un paramètre et renvoie le résultat sous forme de "accumulateur" (appelons-le A - pensez-y comme un int).
Avec ces contraintes (très courantes dans tous les microprocesseurs de cette période), la traduction sera probablement
;i = i+1;
MOV A,(D); //Move in A the content of the memory whose address is in D
ADD A, 1; //The addition of an inlined constant
MOV (D) A; //Move the result back to i (this is the '=' of the expression)
;i+=1;
ADD (D),1; //Add an inlined constant to a memory address stored value
;++i;
INC (D); //Just "tick" a memory located counter
La première façon de procéder est non optimale, mais elle est plus générale lorsque vous utilisez des variables au lieu de constantes ( ADD A, B
ou ADD A, (D+x)
) ou lorsque vous traduisez des expressions plus complexes (elles se résument toutes en une opération push de priorité basse dans une pile, appelez la priorité élevée, pop et répétez jusqu'à ce que tous les arguments aient été éliminés).
La seconde est plus typique de "machine à états": nous ne sommes plus "en train d'évaluer une expression", mais "d'utiliser une valeur": nous utilisons toujours l'ALU, mais nous évitons de déplacer des valeurs car le résultat permettait de remplacer le paramètre. Ce type d'instruction ne peut pas être utilisé lorsque des expressions plus complexes sont nécessaires: i = 3*i + i-2
ne peut pas être utilisé sur place, car il i
est requis plusieurs fois.
Le troisième - même plus simple - n’envisage même pas l’idée «d’addition», mais utilise un circuit plus «primitif» (au sens de calcul) pour un compteur. L'instruction est court-circuitée, se charge plus rapidement et s'exécute immédiatement, car le réseau combinatoire requis pour moderniser un registre afin d'en faire un compteur est plus petit et donc plus rapide que celui d'un additionneur complet.
Avec les compilateurs contemporains (voir C, à présent), permettant l'optimisation du compilateur, la correspondance peut être permutée en fonction de la commodité, mais il existe toujours une différence conceptuelle dans la sémantique.
x += 5
veux dire
- Trouver le lieu identifié par x
- Ajoutez-y 5
Mais x = x + 5
signifie:
- Évaluer x + 5
- Trouver le lieu identifié par x
- Copier x dans un accumulateur
- Ajouter 5 à l'accumulateur
- Stocker le résultat dans x
- Trouver le lieu identifié par x
- Copier l'accumulateur
Bien sûr, l'optimisation peut
- si "trouver x" n'a pas d'effets secondaires, les deux "conclusions" peuvent être effectuées une fois (et x devient une adresse stockée dans un registre de pointeur)
- les deux copies peuvent être supprimées si ADD est appliqué à la
&x
place de l'accumulateur
faisant ainsi coïncider le code optimisé avec celui- x += 5
ci.
Mais cela ne peut être fait que si "trouver x" n'a pas d'effets secondaires, sinon
*(x()) = *(x()) + 5;
et
*(x()) += 5;
sont sémantiquement différentes, puisque x()
les effets secondaires (admettre x()
est une fonction de faire des choses étranges et de renvoyer un int*
) seront produits deux fois ou une fois.
L'équivalence entre x = x + y
et x += y
est donc due au cas particulier où +=
et =
est appliquée à une valeur l directe.
Pour passer à Python, il a hérité de la syntaxe de C, mais comme il n'y a pas de traduction / optimisation AVANT l'exécution dans des langages interprétés, les choses ne sont pas nécessairement intimement liées (puisqu'il y a une étape d'analyse de moins). Cependant, un interprète peut faire référence à différentes routines d'exécution pour les trois types d'expression, en tirant parti d'un code machine différent en fonction de la manière dont l'expression est formée et du contexte d'évaluation.
Pour qui aime plus de détail ...
Chaque unité centrale a une unité ALU (unité arithmétique-logique) qui est, dans son essence même, un réseau combinatoire dont les entrées et les sorties sont "branchées" sur les registres et / ou en mémoire en fonction du code opération de l'instruction.
Les opérations binaires sont généralement implémentées en tant que "modificateur d’un registre accumulateur avec une entrée prise" quelque part ", où quelque part peut se trouver - dans le flux d’instructions lui-même (typique pour le contant manifeste: ADD A 5) - dans un autre registre temporaries: par exemple ADD AB) - dans la mémoire, à une adresse donnée par un registre (typique de la récupération de données, par exemple: ADD A (H)) - H, dans ce cas, fonctionne comme un pointeur de déréférencement.
Avec ce pseudocode, x += 5
est
ADD (X) 5
alors que x = x+5
est
MOVE A (X)
ADD A 5
MOVE (X) A
C'est-à-dire que x + 5 donne un temporaire qui est assigné ultérieurement. x += 5
opère directement sur x.
L'implémentation réelle dépend du jeu d'instructions réel du processeur: s'il n'y a pas de ADD (.) c
code opération, le premier code devient le second: aucun moyen.
S'il existe un tel opcode et que l'optimisation est activée, la seconde expression, après avoir éliminé les déplacements inversés et ajusté l'opcode des registres, devient la première.
x += 5
quex = x + 5
? Ou est-ce vraiment juste du sucre syntaxique comme vous le suggérez?