Existe-t-il une méthode standardisée pour échanger deux variables en Python?

345

En Python, j'ai vu deux valeurs de variables échangées à l'aide de cette syntaxe:

left, right = right, left

Est-ce considéré comme le moyen standard d'échanger deux valeurs de variable ou existe-t-il un autre moyen par lequel deux variables sont, par convention, le plus souvent échangées?

WilliamKF
la source
1
@eyquem: il s'agit simplement de savoir si l' ordre d'évaluation est défini par le langage pour une affectation de tuple / liste. Python le fait, la plupart des anciennes langues ne le font pas.
smci
Hrmm C ++ a swap (a [i], a [k]) pourquoi ne pouvons-nous pas avoir quelque chose comme ça pour Python.
Nils

Réponses:

389

Python évalue les expressions de gauche à droite. Notez que lors de l'évaluation d'une affectation, le côté droit est évalué avant le côté gauche.

http://docs.python.org/3/reference/expressions.html#evaluation-order

Cela signifie ce qui suit pour l'expression a,b = b,a:

  • le côté droit b,aest évalué, c'est-à-dire qu'un tuple de deux éléments est créé dans la mémoire. Les deux éléments sont les objets désignés par les identifiants bet aqui existaient avant que l'instruction ne soit rencontrée lors d'une exécution de programme
  • juste après la création de ce tuple, aucune affectation de cet objet tuple n'a encore été faite, mais peu importe, Python sait en interne où il se trouve
  • puis, le côté gauche est évalué, c'est-à-dire que le tuple est affecté au côté gauche
  • comme le côté gauche est composé de deux identifiants, le tuple est décompressé afin que le premier identifiant asoit attribué au premier élément du tuple (qui est l'objet qui était formellement b avant le swap parce qu'il avait un nom b)
    et le le deuxième identifiant best attribué au deuxième élément du tuple (qui est l'objet qui était auparavant un avant le swap car ses identifiants l'étaient a)

Ce mécanisme a effectivement échangé les objets attribués aux identifiants aetb

Donc, pour répondre à votre question: OUI, c'est la manière standard d'échanger deux identifiants sur deux objets.
Soit dit en passant, les objets ne sont pas des variables, ce sont des objets.

eyquem
la source
1
Pour autant que je sache, l'échange de deux variables de cette manière n'utilise PAS de mémoire supplémentaire, juste de la mémoire pour 2 variables elles-mêmes, ai-je raison?
Catbuilts
1
@Catbuilts La construction du tuple prendra de la mémoire supplémentaire (probablement plus que la version à 3 variables du swap), mais comme les seules choses échangées sont des adresses mémoire, ce ne sera pas beaucoup de mémoire supplémentaire dans l'absolu. sens (peut-être 24 octets supplémentaires).
Brilliand
@Brilliand: Merci. Avez-vous des documents à ce sujet. C'est assez intéressant et j'aimerais avoir une lecture plus approfondie. Merci.
Catbuilts
1
@Catbuilts Je ne suis pas sûr, mais cela pourrait aider à lire comment fonctionnent les pointeurs C ++. Alors que Python essaie de faire automatiquement les choses de la meilleure façon pour vous, C ++ vous donne en fait toutes les options pour faire les choses de toutes les bonnes et de mauvaises façons, c'est donc un bon point de départ pour apprendre quels sont les inconvénients de l'approche que Python prend . Souvenez-vous également que le fait d'avoir un système d'exploitation "64 bits" signifie que le stockage d'une adresse mémoire prend 64 bits de mémoire - c'est en partie de là que j'ai obtenu mon numéro "24 octets".
Brilliand
Grande explication. Ajoutant simplement que c'est pourquoi vous pouvez également utiliser cette méthode pour réorganiser un certain nombre de "variables", par exemple a, b, c = c, a, b.
alexlomba87
109

C'est la façon standard d'échanger deux variables, oui.

Martijn Pieters
la source
38

Je connais trois façons d'échanger des variables, mais a, b = b, a c'est la plus simple. Il y a

XOR (pour les entiers)

x = x ^ y
y = y ^ x
x = x ^ y

Ou de façon concise,

x ^= y
y ^= x
x ^= y

Variable temporaire

w = x
x = y
y = w
del w

Échange de tuple

x, y = y, x
Il n'y a personne
la source
1
Est le plus simple et le seul qui n'est pas obscurci.
Jorge Leitao
17
Le XOR n'échange pas les "variables". Il échange des variables entières. (Ou les quelques autres types implémentant correctement l'opérateur XOR) De plus, puisque selon la réponse de Rogalski, le Tuple Swap est optimisé dans l'interpréteur, il n'y a vraiment rien contre. Court, clair et rapide.
Rawler
Le problème XOR peut être évité par l'utilisation de l'opérateur + -, mais je pense toujours que le mieux est a, b = b, a codex = x + yy = xy x = xycode
ashish
22

Je ne dirais pas que c'est un moyen standard d'échanger car cela entraînera des erreurs inattendues.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]sera modifié en premier, puis affectera la deuxième variable nums[nums[i] - 1].

Yi Huang
la source
2
Vous avez le problème dans presque tous les langages de programmation, ce n'est pas sûr d'utiliser swap (a, b), si a dépend de b ou vice versa. Par exemple, swap (a, b) pourrait être étendu à: var c=a, a=b, b=c. Et puis, la dernière affectation utilisera la nouvelle valeur de apour évaluer l'adresse de b.
Kai Petzke
1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Résoudrait le problème.
Bill Cheng
1
Cela résout le problème, mais pourquoi?
Jackson Kelley
2
@JacksonKelley L'évaluation du côté droit est sûre. Dans nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: Le problème est lorsque python effectue l'affectation de gauche, nums[i]est modifié, ce qui entraîne une nums[nums[i] - 1]modification inattendue. Vous pouvez imaginer au début que vous voulez nums[1],nums[2] = nums[2],nums[1], mais après la nums[1] = nums[2]course, vous n'en avez plus nums[2] = nums[1], à la place vous avez nums[888] = nums[1].
guo
5

Ne fonctionne pas pour les tableaux multidimensionnels, car les références sont utilisées ici.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

Voir aussi Swap slices of Numpy arrays

gizzmole
la source
Il s'agit en effet d'une fonction spéciale (ou bug) de la bibliothèque numpy.
Kai Petzke
-1

Pour contourner les problèmes expliqués par eyquem , vous pouvez utiliser le copymodule pour renvoyer un tuple contenant des copies (inversées) des valeurs, via une fonction:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

Même fonction qu'un lambda:

swapper = lambda x, y: (copy(y), copy(x))

Ensuite, attribuez-les aux noms souhaités, comme ceci:

x, y = swapper(y, x)

REMARQUE: si vous le souhaitez, vous pouvez importer / utiliser deepcopyau lieu de copy.

LogicalBranch
la source
quel est le problème que vous essayez de résoudre par copie?
Hanan Shteingart
Ceux discutés dans le post d'Eyquem .
LogicalBranch
1
mais cela ne pose aucun problème, en fait il dit "OUI, c'est la manière standard d'échanger deux identifiants"
Hanan Shteingart
-2

Vous pouvez combiner des swaps de tuple et XOR : x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

ou

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Utilisation de lambda :

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Production:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
u-betcha
la source