• Jobs
  • About Us
  • professionals
    • Home
    • Jobs
    • Courses and challenges
  • business
    • Home
    • Post vacancy
    • Our process
    • Pricing
    • Assessments
    • Payroll
    • Blog
    • Sales
    • Salary Calculator

0

234
Views
Comprenda el intercambio de Python: ¿por qué a, b = b, a no siempre es equivalente a b, a = a, b?

Como todos sabemos, la forma pitónica de intercambiar los valores de dos elementos a y b es

 a, b = b, a

y debe ser equivalente a

 b, a = a, b

Sin embargo, hoy, cuando estaba trabajando en un código, descubrí accidentalmente que los siguientes dos intercambios dan resultados diferentes:

 nums = [1, 2, 4, 3] i = 2 nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i] print(nums) # [1, 2, 4, 3] nums = [1, 2, 4, 3] i = 2 nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] print(nums) # [1, 2, 3, 4]

Esto es alucinante para mí. ¿Alguien puede explicarme qué pasó aquí? Pensé que en un intercambio de Python las dos asignaciones ocurren de forma simultánea e independiente.

over 3 years ago · Santiago Trujillo
8 answers
Answer question

0

Para comprender el orden de evaluación, hice una clase 'Variable' que imprime cuando se establece y se obtiene su 'valor'.

 class Variable: def __init__(self, name, value): self._name = name self._value = value @property def value(self): print(self._name, 'get', self._value) return self._value @value.setter def value(self): print(self._name, 'set', self._value) self._value = value a = Variable('a', 1) b = Variable('b', 2) a.value, b.value = b.value, a.value

Cuando se ejecuta da como resultado:

 b get 2 a get 1 a set 2 b set 1

Esto muestra que primero se evalúa el lado derecho (de izquierda a derecha) y luego se evalúa el lado izquierdo (nuevamente, de izquierda a derecha).

Con respecto al ejemplo de OP: el lado derecho evaluará los mismos valores en ambos casos. Se establece el primer término del lado izquierdo y esto afecta la evaluación del segundo término. Nunca se evaluó de manera simultánea e independiente, es solo que la mayoría de las veces que se usa esto, los términos no dependen unos de otros. Establecer un valor en una lista y luego tomar un valor de esa lista para usarlo como índice en la misma lista generalmente no es una cosa y si eso es difícil de entender, lo entiende. Como cambiar la longitud de una lista en un bucle for es malo, esto tiene el mismo olor. (Sin embargo, una pregunta estimulante, como habrás adivinado porque corrí hacia un bloc de notas)

over 3 years ago · Santiago Trujillo Report

0

Una forma de analizar fragmentos de código en CPython es desmontar su código de bytes para su máquina de pila simulada.

 >>> import dis >>> dis.dis("nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]") 1 0 LOAD_NAME 0 (nums) 2 LOAD_NAME 0 (nums) 4 LOAD_NAME 1 (i) 6 BINARY_SUBSCR 8 LOAD_CONST 0 (1) 10 BINARY_SUBTRACT 12 BINARY_SUBSCR 14 LOAD_NAME 0 (nums) 16 LOAD_NAME 1 (i) 18 BINARY_SUBSCR 20 ROT_TWO 22 LOAD_NAME 0 (nums) 24 LOAD_NAME 1 (i) 26 STORE_SUBSCR 28 LOAD_NAME 0 (nums) 30 LOAD_NAME 0 (nums) 32 LOAD_NAME 1 (i) 34 BINARY_SUBSCR 36 LOAD_CONST 0 (1) 38 BINARY_SUBTRACT 40 STORE_SUBSCR 42 LOAD_CONST 1 (None) 44 RETURN_VALUE

Agregué las líneas en blanco para facilitar la lectura. Las dos expresiones de búsqueda se calculan en los bytes 0-13 y 14-19. BINARY_SUBSCR reemplaza los dos valores superiores en la pila, un objeto y un subíndice, con el valor obtenido del objeto. Los dos valores obtenidos se intercambian para que el primero calculado sea el primer límite. Las dos operaciones de almacenamiento se realizan en los bytes 22-27 y 28-41. STORE_SUBSCR usa y elimina los tres primeros valores de la pila, un valor para almacenar, un objeto y un subíndice. (La parte de retorno Ninguno aparentemente siempre se agrega al final). La parte importante de la pregunta es que los cálculos para las tiendas se realizan secuencialmente en lotes separados e independientes.

La descripción más cercana en Python del cálculo de CPython requiere la introducción de una variable de pila

 stack = [] stack.append(nums[nums[i]-1]) stack.append(nums[i]) stack.reverse() nums[i] = stack.pop() nums[nums[i]-1] = stack.pop()

Aquí está el desmontaje de la declaración invertida

 >>> dis.dis("nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]") 1 0 LOAD_NAME 0 (nums) 2 LOAD_NAME 1 (i) 4 BINARY_SUBSCR 6 LOAD_NAME 0 (nums) 8 LOAD_NAME 0 (nums) 10 LOAD_NAME 1 (i) 12 BINARY_SUBSCR 14 LOAD_CONST 0 (1) 16 BINARY_SUBTRACT 18 BINARY_SUBSCR 20 ROT_TWO 22 LOAD_NAME 0 (nums) 24 LOAD_NAME 0 (nums) 26 LOAD_NAME 1 (i) 28 BINARY_SUBSCR 30 LOAD_CONST 0 (1) 32 BINARY_SUBTRACT 34 STORE_SUBSCR 36 LOAD_NAME 0 (nums) 38 LOAD_NAME 1 (i) 40 STORE_SUBSCR 42 LOAD_CONST 1 (None) 44 RETURN_VALUE
over 3 years ago · Santiago Trujillo Report

0

Me parece que esto solo sucedería cuando el contenido de la lista esté en el rango de índices de lista para la lista. Si por ejemplo:

 nums = [10, 20, 40, 30]

El código fallará con:

 >>> nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range

Así que definitivamente, un gotcha. Nunca use nunca el contenido de una lista como índice de esa lista.

over 3 years ago · Santiago Trujillo Report

0

Esto se debe a que la evaluación, específicamente en el lado izquierdo de = , ocurre de izquierda a derecha:

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

Primero se asigna nums[i] , y luego ese valor se usa para determinar el índice en la asignación a nums[nums[i]-1]

Al hacer la tarea así:

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

... el índice de nums[nums[i]-1] depende del valor antiguo de nums[i] , ya que la asignación a nums[i] sigue después...

over 3 years ago · Santiago Trujillo Report

0

De python.org

La asignación de un objeto a una lista de destino, opcionalmente entre paréntesis o corchetes, se define recursivamente de la siguiente manera.

...

  • De lo contrario: el objeto debe ser iterable con la misma cantidad de elementos que objetivos en la lista de objetivos, y los elementos se asignan, de izquierda a derecha, a los objetivos correspondientes.

Así que interpreto que eso significa que su tarea

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

es aproximadamente equivalente a

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

(con una mejor verificación de errores, por supuesto)

mientras que el otro

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

es como

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

Entonces, el lado derecho se evalúa primero en ambos casos. Pero luego las dos piezas del lado izquierdo se evalúan en orden y las asignaciones se realizan inmediatamente después de la evaluación. Crucialmente, esto significa que el segundo término en el lado izquierdo solo se evalúa después de que ya se haya realizado la primera asignación. Entonces, si actualiza nums[i] primero, entonces nums[nums[i] - 1] se refiere a un índice diferente que si actualiza nums[i] segundo lugar.

over 3 years ago · Santiago Trujillo Report

0

Esto sucede de acuerdo con las reglas:

  • El lado derecho se evalúa primero.
  • Luego, cada valor del lado izquierdo obtiene su nuevo valor, de izquierda a derecha.

Entonces, con nums = [1, 2, 4, 3] , tu código en el primer caso

 nums[2], nums[nums[2]-1] = nums[nums[2]-1], nums[2]

es equivalente a:

 nums[2], nums[nums[2]-1] = nums[nums[2]-1], nums[2] nums[2], nums[nums[2]-1] = nums[3], nums[2] nums[2], nums[nums[2]-1] = 3, 4

y como ahora se evalúa el lado derecho, las asignaciones son equivalentes a:

 nums[2] = 3 nums[nums[2]-1] = 4 nums[2] = 3 nums[3-1] = 4 nums[2] = 3 nums[2] = 4

lo que da:

 print(nums) # [1, 2, 4, 3]

En el segundo caso, obtenemos:

 nums[nums[2]-1], nums[2] = nums[2], nums[nums[2]-1] nums[nums[2]-1], nums[2] = nums[2], nums[3] nums[nums[2]-1], nums[2] = 4, 3 nums[nums[2]-1] = 4 nums[2] = 3 nums[4-1] = 4 nums[2] = 3 nums[3] = 4 nums[2] = 3 print(nums) # [1, 2, 3, 4]
over 3 years ago · Santiago Trujillo Report

0

En el lado izquierdo de su expresión, está leyendo y escribiendo números [i], no sé si Python garantiza el procesamiento de las operaciones de desempaquetado en orden de izquierda a derecha, pero supongamos que sí, su primer ejemplo sería equivalente a.

 t = nums[nums[i]-1], nums[i] # t = (3,4) nums[i] = t[0] # nums = [1,2,3,3] n = nums[i]-1 # n = 2 nums[n] = t[1] # nums = [1,2,4,3]

Mientras que su segundo ejemplo sería equivalente a

 t = nums[i], nums[nums[i]-1] # t = (4,3) n = nums[i]-1 # n = 3 nums[n] = t[0] # nums = [1,2,4,4] nums[i] = t[0] # nums = [1,2,3,4]

Lo cual es consistente con lo que tienes.

over 3 years ago · Santiago Trujillo Report

0

Thierry dio una buena respuesta, déjame ser más claro. Tenga en cuenta que si nums = [1, 2, 4, 3] ,

en este código:

 nums[nums[i]-1], nums[i]
  • yo tengo 2,
  • nums[nums[i]-1] es nums[4-1], entonces nums[3], (el valor es 3)
  • nums[i] es nums[2], (el valor es 4)
  • el resultado es: (3, 4)

en este código:

 nums[i], nums[nums[i]-1]
  • nums[i] es nums[2] se convierte en 3, (=>[1, 2, 3 , 3])
  • pero nums[nums[i]-1] no es nums[ 4 -1] sino nums[ 3 -1], por lo que nums[2] también se convierte en (de vuelta a) 4 (=>[1, 2, 4 , 3 ])

Quizás la buena pregunta sobre un intercambio fue usar:

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

Intentalo:

 >>> print(nums) >>> [1, 2, 4, 3] >>> nums[i], nums[i-1] = nums[i-1], nums[i] >>> print(nums) >>> [1, 4, 2, 3]

ChD

over 3 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Our process Sales
Legal
Terms and conditions Privacy policy
© 2025 PeakU Inc. All Rights Reserved.

Andres GPT

Recommend me some offers
I have an error