Python中两个变量交换位置的原理

2 阅读5分钟

Python中两个变量交换位置的原理


一、Python 的交换写法

1. 一行交换(推荐)

a, b = b, a

这是 Python 独有的优雅写法,一行搞定,不需要临时变量。

a = 10
b = 20
a, b = b, a
print(a, b)   # 20 10

2. 用临时变量(其他语言常见)

temp = a
a = b
b = temp

C、Java 等语言通常这么写,因为不能直接 a, b = b, a

3. 用加减法(不用临时变量,但可能溢出)

a = a + b
b = a - b
a = a - b

不推荐,容易溢出,可读性差。

4. 用异或(位运算,不用临时变量)

a = a ^ b
b = a ^ b
a = a ^ b

不推荐,只适用于整数,可读性差。


二、为什么 a, b = b, a 能工作?

关键在于:等号右边先整体求值,再解包赋值。

执行步骤

a, b = b, a
  1. 先算右边:把 b, a 组成一个元组 (b, a)
  2. 再解包赋值:把元组的第一个元素给 a,第二个给 b
a = 10
b = 20

# 第一步:右边组成元组 (20, 10)
# 第二步:a = 20, b = 10
a, b = b, a

关键:右边在赋值前就已经算好了,所以不会出现“a 被覆盖后 b 拿不到原 a”的问题。

对比错误写法

a = 10
b = 20

a = b     # a = 20
b = a     # b = 20,错了!a 已经是 20 了
print(a, b)   # 20 20

错误原因a = b 后,a 的原值 10 丢了,b = a 拿到的是 20。

正确写法

a, b = b, a   # 右边先算好 (20, 10),再赋值
print(a, b)   # 20 10

三、本质:元组解包

a, b = b, a 其实是元组解包的语法糖:

# 等价于
temp = (b, a)   # 右边组成元组
a = temp[0]
b = temp[1]

Python 的赋值规则是:先算右边,再解包给左边

更多解包例子

# 三个变量交换
a, b, c = c, a, b

# 从元组解包
x, y = (1, 2)

# 从列表解包
x, y = [1, 2]

# 星号解包
first, *rest = [1, 2, 3, 4]
# first = 1, rest = [2, 3, 4]

四、在冒泡排序里的作用

if arr[j] > arr[j + 1]:
    arr[j], arr[j + 1] = arr[j + 1], arr[j]

执行过程

  1. 右边 arr[j + 1], arr[j] 先组成元组 (arr[j+1], arr[j])
  2. 再解包赋值:arr[j] = arr[j+1]arr[j+1] = arr[j]

效果:两个元素互换位置。

图解

交换前:
arr[j] = 5, arr[j+1] = 3

右边先算:(3, 5)

赋值:
arr[j] = 3
arr[j+1] = 5

交换后:
arr[j] = 3, arr[j+1] = 5

完整例子

arr = [5, 3, 8, 1]

j = 0
# arr[0] = 5, arr[1] = 3
if arr[0] > arr[1]:   # 5 > 3,交换
    arr[0], arr[1] = arr[1], arr[0]

print(arr)   # [3, 5, 8, 1]

五、交换的其他应用

1. 交换列表元素

lst = [1, 2, 3, 4, 5]

# 交换索引 0 和 4
lst[0], lst[4] = lst[4], lst[0]
print(lst)   # [5, 2, 3, 4, 1]

# 反转列表(手动)
lst = [1, 2, 3, 4, 5]
left, right = 0, len(lst) - 1
while left < right:
    lst[left], lst[right] = lst[right], lst[left]
    left += 1
    right -= 1
print(lst)   # [5, 4, 3, 2, 1]

2. 交换字典的值

d = {"a": 1, "b": 2}
d["a"], d["b"] = d["b"], d["a"]
print(d)   # {'a': 2, 'b': 1}

3. 交换多个变量

a, b, c = 1, 2, 3
a, b, c = c, a, b
print(a, b, c)   # 3 1 2

# 循环移位
a, b, c, d = 1, 2, 3, 4
a, b, c, d = d, a, b, c
print(a, b, c, d)   # 4 1 2 3

4. 在排序算法里交换

选择排序

def selection_sort(arr):
    n = len(arr)
    for i in range(n - 1):
        min_idx = i
        for j in range(i + 1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        arr[i], arr[min_idx] = arr[min_idx], arr[i]   # 交换
    return arr

快速排序的 partition

def partition(arr, low, high):
    pivot = arr[high]
    i = low - 1
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]   # 交换
    arr[i + 1], arr[high] = arr[high], arr[i + 1]   # 交换
    return i + 1

六、性能说明

a, b = b, a 在 Python 里非常高效,底层是 C 实现的元组创建和解包。

import timeit

# 元组解包交换
t1 = timeit.timeit('a, b = b, a', setup='a, b = 1, 2', number=1000000)

# 临时变量交换
t2 = timeit.timeit('temp = a; a = b; b = temp', setup='a, b = 1, 2', number=1000000)

print(t1, t2)   # 元组解包通常更快或相当

结论a, b = b, a 既简洁又高效,优先用。


七、常见坑

1. 左右数量不匹配

a, b = 1, 2, 3   # ❌ ValueError: too many values to unpack
a, b, c = 1, 2   # ❌ ValueError: not enough values to unpack

2. 交换时右边用了左边的新值

a = 1
b = 2
a, b = b, a   # ✅ 右边先算,没问题

# 但这样写会出错
a = 1
b = 2
a = b         # a = 2
b = a         # b = 2,错了

记住:交换必须一行写完,不要拆成两行。

3. 交换不可变对象

s = "hello"
# s[0], s[1] = s[1], s[0]   # ❌ TypeError,字符串不可变

字符串、元组不可变,不能这样交换元素。要转成 list 再交换。


八、一句话总结

问题答案
Python 怎么交换?a, b = b, a
为什么能工作?右边先组成元组,再解包赋值
和临时变量比?更简洁,性能相当或更好
本质是什么?元组解包的语法糖
能交换多个吗?能,a, b, c = c, a, b
能交换列表元素吗?能,lst[i], lst[j] = lst[j], lst[i]
最大坑?不能拆成两行写;不可变对象不能交换元素

核心记住:

  1. Python 交换两个变量用 a, b = b, a,一行搞定。
  2. 原理是右边先整体求值成元组,再解包赋值
  3. 冒泡排序里 arr[j], arr[j+1] = arr[j+1], arr[j] 就是交换相邻元素。
  4. 不能拆成两行,否则原值被覆盖。
  5. 字符串、元组不可变,不能直接交换元素。