是否有标准化的方法可以在Python中交换两个变量?


345

在Python中,我已经看到使用此语法交换了两个变量值:

left, right = right, left

这是否被认为是交换两个变量值的标准方法,或者是否有其他一些方式可以按照惯例最通常地交换两个变量?


1
@eyquem:简单地归结为是否由元组/列表分配的语言定义了评估顺序。Python可以,大多数较旧的语言则不可以。
smci

Hrmm C ++具有swap(a [i],a [k]),为什么我们不能在Python中使用类似的东西。
尼尔斯

Answers:


389

Python从左到右计算表达式。请注意,在评估分配时,右侧的评估先于左侧。

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

这意味着该表达式的以下内容a,b = b,a

  • 对右侧b,a进行求值,即在内存中创建两个元素的元组。这两个元素是由标识符b和所指定的对象,这些对象a在程序执行期间对指令进行加密之前就已存在
  • 创建该元组后,仍未分配该元组对象,但这没关系,Python内部知道它在哪里
  • 然后,评估左侧,即将元组分配给左侧
  • 由于左侧是由两个标识符组成的,因此将元组解压缩,以便将第一个标识符a分配给元组的第一个元素(这是交换之前为b的对象,因为它具有名称b
    ,并且第二个标识符b分配给元组的第二个元素(该对象以前是交换之前的a,因为其标识符为a

该机制有效地交换了分配给标识符的对象,a并且b

因此,回答您的问题:是的,这是在两个对象上交换两个标识符的标准方法。
顺便说一下,对象不是变量,而是对象。


1
据我了解,以这种方式交换两个变量不会占用额外的内存,只是两个变量本身的内存,对吗?
Catbuilts

1
@Catbuilts构造元组将占用一些额外的内存(可能比交换的3变量版本要占用的内存更多),但是由于交换的唯一内容是内存地址,因此绝对不会占用太多内存感觉(也许额外的24个字节)。
Brilliand19年

@Brilliand:谢谢。您有与此主题有关的任何文件吗?这很有趣,我想进一步阅读。谢谢。
Catbuilts

1
@Catbuilts我不确定,但它可能有助于阅读C ++指针的工作原理。Python会尝试自动为您自动执行最佳方法,而C ++实际上为您提供了以所有可能的对与错方式进行操作的所有选项,因此这是了解Python所采用方法的弊端的一个很好的起点。还要记住,拥有“ 64位”操作系统意味着存储内存地址会占用64位内存-这就是我从中获取“ 24字节”数字的一部分。
Brilliand19

很好的解释。只需补充一点,这就是为什么您还可以使用此方法重新排列任意数量的“变量”的原因,例如a, b, c = c, a, b
alexlomba87


38

我知道三种交换变量的方法,但是a, b = b, a最简单。有

XOR(整数)

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

或简而言之,

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

临时变量

w = x
x = y
y = w
del w

元组交换

x, y = y, x

1
是最简单且唯一不会混淆的对象。
豪尔赫·雷陶

17
XOR不会交换“变量”。它交换整数变量。(或者其他少数可以正确实现XOR运算符的类型)此外,由于根据Rogalski的回答,在解释器中对Tuple Swap进行了优化,因此实际上没有什么反对它的。简短,清晰,快速。
Rawler '16

可以通过+-运算符使用来避免XOR问题,但我仍然感觉最好的是a,b = b,a codex = x + yy = xy x = xycode
ashish

22

我不会说这是一种标准的交换方式,因为它将导致一些意外错误。

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

nums[i]将首先被修改,然后影响第二个变量nums[nums[i] - 1]


2
您几乎在任何一种编程语言中都有问题,如果a取决于b,反之亦然,使用swap(a,b)是不安全的。例如,交换(A,B)可被扩展为:var c=aa=bb=c。然后,最后一次赋值将使用新值of a评估b的地址。
Kai Petzke

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]。将解决问题。
Bill Cheng

1
这确实解决了问题,但是为什么呢?
杰克逊·凯利

2
@JacksonKelley评估右侧是安全的。在中nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]:问题是python在进行左侧分配时nums[i]发生了更改,这导致nums[nums[i] - 1]了意外更改。您最初可以想象自己想要的 nums[1],nums[2] = nums[2],nums[1],但是nums[1] = nums[2]跑步后,您不再拥有nums[2] = nums[1],而是得到了nums[888] = nums[1]

5

不适用于多维数组,因为此处使用了引用。

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)

另请参见交换Numpy数组的切片


这确实是numpy库的特殊功能(或错误)。
Kai Petzke

-1

为了解决eyquem解释的问题,您可以使用copy模块通过一个函数返回一个包含(反向)值副本的元组:

from copy import copy

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

与的功能相同lambda

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

然后,将它们分配给所需的名称,如下所示:

x, y = swapper(y, x)

注意:如果需要,可以导入/使用deepcopy而不是copy


您要通过复印解决的问题是什么?
Hanan Shteingart


1
但这并没有说明任何问题,实际上他说“是的,这是交换两个标识符的标准方法”
Hanan Shteingart

-2

您可以组合元组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))

要么

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

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

输出:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.