¿Existe un método estandarizado para intercambiar dos variables en Python?

345

En Python, he visto dos valores variables intercambiados usando esta sintaxis:

left, right = right, left

¿Se considera esta la forma estándar de intercambiar dos valores de variables o hay algún otro medio por el cual dos variables se intercambian por convención más habitualmente?

WilliamKF
fuente
1
@eyquem: simplemente se reduce a si el orden de evaluación está definido por el lenguaje para una asignación de tupla / lista. Python sí, la mayoría de los lenguajes antiguos no.
smci
Hrmm C ++ tiene swap (a [i], a [k]) ¿por qué no podemos tener algo así para Python?
Nils

Respuestas:

389

Python evalúa expresiones de izquierda a derecha. Tenga en cuenta que al evaluar una tarea, el lado derecho se evalúa antes que el lado izquierdo.

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

Eso significa lo siguiente para la expresión a,b = b,a:

  • b,ase evalúa el lado derecho , es decir, se crea una tupla de dos elementos en la memoria. Los dos elementos son los objetos designados por los identificadores by a, que existían antes de que se fomente la instrucción durante la ejecución del programa.
  • justo después de la creación de esta tupla, todavía no se ha asignado este objeto de tupla, pero no importa, Python sabe internamente dónde está
  • luego, se evalúa el lado izquierdo, es decir, la tupla se asigna al lado izquierdo
  • Como el lado izquierdo está compuesto por dos identificadores, la tupla se desempaqueta para que el primer identificador ase asigne al primer elemento de la tupla (que es el objeto que antes era b antes del intercambio porque tenía nombre b)
    y el el segundo identificador bse asigna al segundo elemento de la tupla (que es el objeto que anteriormente era un antes del intercambio porque sus identificadores eran a)

Este mecanismo ha intercambiado efectivamente los objetos asignados a los identificadores ayb

Entonces, para responder a su pregunta: SÍ, es la forma estándar de intercambiar dos identificadores en dos objetos.
Por cierto, los objetos no son variables, son objetos.

eyquem
fuente
1
Según tengo entendido, el intercambio de dos variables de esta manera NO usa memoria adicional, solo memoria para 2 variables, ¿estoy en lo cierto?
Catbuilts
1
@Catbuilts La construcción de la tupla ocupará algo de memoria adicional (probablemente más de lo que ocuparía la versión de 3 variables del intercambio), pero dado que las únicas cosas que se intercambian son las direcciones de memoria, no será mucha memoria adicional en absoluto sentido (tal vez 24 bytes adicionales).
Brilliand
@Brilliand: Gracias. ¿Tiene algún documento para este tema? Es bastante interesante y me gustaría leer más. Gracias.
Catbuilts
1
@Catbuilts No estoy seguro, pero podría ser útil leer cómo funcionan los punteros de C ++. Mientras que Python intenta hacer las cosas automáticamente de la mejor manera para usted, C ++ en realidad le ofrece todas las opciones para hacer las cosas de todas las formas correctas e incorrectas posibles, por lo que es un buen punto de partida para aprender cuáles son los inconvenientes del enfoque que adopta Python . También recuerde que tener un sistema operativo de "64 bits" significa que almacenar una dirección de memoria ocupa 64 bits de memoria, eso es parte de donde obtuve mi número de "24 bytes".
Brilliand
Gran explicación Simplemente agregando que es por eso que también puede usar este método para reorganizar cualquier número de "variables", por ejemplo a, b, c = c, a, b.
alexlomba87
109

Esa es la forma estándar de intercambiar dos variables, sí.

Martijn Pieters
fuente
38

Sé tres formas de intercambiar variables, pero a, b = b, aes la más simple. Ahi esta

XOR (para enteros)

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

O concisamente

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

Variable temporal

w = x
x = y
y = w
del w

Tuple swap

x, y = y, x
No hay nadie aquí
fuente
1
Es el más simple y el único que no está ofuscado.
Jorge Leitao
17
El XOR no intercambia "variables". Intercambia variables enteras. (O los otros pocos tipos que implementan correctamente el operador XOR). Además, dado que según la respuesta de Rogalski, el Tuple Swap está optimizado en el intérprete, realmente no hay nada en contra. Corto, claro y rápido.
Rawler
El problema de XOR se puede evitar con el uso del operador + -, pero aún así me siento mejor es a, b = b, a codex = x + aa = xy x = xycode
ashish
22

No diría que es una forma estándar de intercambio porque provocará algunos errores inesperados.

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

nums[i]se modificará primero y luego afectará a la segunda variable nums[nums[i] - 1].

Yi Huang
fuente
2
Tiene el problema en casi cualquier lenguaje de programación, es decir, no es seguro usar swap (a, b), si a depende de bo o viceversa. Por ejemplo, swap (a, b) podría ampliarse a: var c=a, a=b, b=c. Y luego, la última asignación utilizará el nuevo valor de apara evaluar la dirección de b.
Kai Petzke
1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Resolvería el problema.
Bill Cheng
1
Esto resuelve el problema, pero ¿por qué?
Jackson Kelley
2
@JacksonKelley La evaluación del lado derecho es segura. En nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: El problema es cuando python hace la asignación del lado izquierdo, nums[i]se cambia, lo que provoca un nums[nums[i] - 1]cambio inesperado. Puedes imaginarte al principio que quieres nums[1],nums[2] = nums[2],nums[1], pero después de nums[1] = nums[2]correr, ya no tienes nums[2] = nums[1], sino que tienes nums[888] = nums[1].
guo
5

No funciona para matrices multidimensionales, porque aquí se usan referencias.

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)

Ver también Intercambiar rebanadas de matrices Numpy

gizzmole
fuente
Esta es de hecho una característica especial (o error) de la biblioteca numpy.
Kai Petzke
-1

Para evitar los problemas explicados por eyquem , puede usar el copymódulo para devolver una tupla que contiene copias (invertidas) de los valores, a través de una función:

from copy import copy

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

La misma función que un lambda:

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

Luego, asigne esos a los nombres deseados, así:

x, y = swapper(y, x)

NOTA: si lo desea, puede importar / usar en deepcopylugar de copy.

Rama Lógica
fuente
¿Cuál es el problema que estás tratando de resolver copiando?
Hanan Shteingart
Los discutidos en la publicación de eyquem .
LogicalBranch
1
pero no indica ningún problema, de hecho dice "SÍ, es la forma estándar de intercambiar dos identificadores"
Hanan Shteingart
-2

Se pueden combinar tuplas y XOR permutas: 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))

o

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))

Usando 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))

Salida:

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