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
- 先算右边:把
b, a组成一个元组(b, a)。 - 再解包赋值:把元组的第一个元素给
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]
执行过程:
- 右边
arr[j + 1], arr[j]先组成元组(arr[j+1], arr[j])。 - 再解包赋值:
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] |
| 最大坑? | 不能拆成两行写;不可变对象不能交换元素 |
核心记住:
- Python 交换两个变量用
a, b = b, a,一行搞定。 - 原理是右边先整体求值成元组,再解包赋值。
- 冒泡排序里
arr[j], arr[j+1] = arr[j+1], arr[j]就是交换相邻元素。 - 不能拆成两行,否则原值被覆盖。
- 字符串、元组不可变,不能直接交换元素。