python变量交换

python数值交换不推荐使用中间值,使用Pythonic的实现不仅简洁,而且执行效率高。

中间值方法

交换两个变量的值,我们熟悉的方法如下:

1
2
3
temp = x
x = y
y = temp

Pythonic 实现方式

python中有Pythonic的实现方式:

1
x, y = y, x

交换效率

我们使用timeit查看下两则的交换效率

1
2
3
4
5
>>> from timeit import Timer
>>> Timer("x, y = y, x", "x=2;y=3").timeit()
0.051779985427856445
>>> Timer("temp=x;x=y;y=temp", "x=2; y=3").timeit()
0.06303882598876953

可以看到Pythonic的实现不仅简洁,而且执行效率高

dis分析

使用dis分析python执行的字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
import dis
def swap1():
x, y = 2, 3
x, y = y, x

def swap2():
x, y = 2, 3
temp = x
x = y
y = temp

dis.dis(swap1)
dis.dis(swap2)

结果分别如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> dis.dis(swap1)
2 0 LOAD_CONST 3 ((2, 3))
3 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (x)
9 STORE_FAST 1 (y)

3 12 LOAD_FAST 1 (y)
15 LOAD_FAST 0 (x)
18 ROT_TWO
19 STORE_FAST 0 (x)
22 STORE_FAST 1 (y)
25 LOAD_CONST 0 (None)
28 RETURN_VALUE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> dis.dis(swap2)
2 0 LOAD_CONST 3 ((2, 3))
3 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (x)
9 STORE_FAST 1 (y)

3 12 LOAD_FAST 0 (x)
15 STORE_FAST 2 (temp)

4 18 LOAD_FAST 1 (y)
21 STORE_FAST 0 (x)

5 24 LOAD_FAST 2 (temp)
27 STORE_FAST 1 (y)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE

通过字节码可以看出:swap1使用了ROT_TWO指令,而swap2比swap1多执行了一个LOAD_FAST+STORE_FAST(入栈和赋值)。

那么ROT_TWO做了什么事情,使得变量能够成功交换,且比LOAD_FAST+STORE_FAST效率高呢?

查看官方api,它的作用是交换栈顶的两个元素:

1
2
ROT_TWO()
Swaps the two top-most stack items.

而ROT_TWO在cpython的实现如下:

1
2
3
4
5
6
7
TARGET(ROT_TWO) {
PyObject *top = TOP();
PyObject *second = SECOND();
SET_TOP(second);
SET_SECOND(top);
FAST_DISPATCH();
}

SET_TOP和SET_SECOND的定义:

1
2
#define SET_TOP(v)        (stack_pointer[-1] = (v))
#define SET_SECOND(v) (stack_pointer[-2] = (v))

栈存放的是对象的指针,那么x,y=y,x所做的就是将栈顶的两个指针互换了一下,从而实现了x和y值的交换。而直接使用指针进行交换,就是其为什么执行速度会更快了。