本文转载自:https://www.jianshu.com/p/0a80f376c7f3

简书不维护了,欢迎关注我的知乎:波罗学的个人主页

知乎地址:https://zhuanlan.zhihu.com/p/58691979

在编程中,一旦提到变量值的交换,脑海中最先浮现的做法就是引入一个临时变量作为媒介来做,来看看具体的实现。

解决方案

先假设有两个变量x、y,如下:

x = 10
y = 20

常见方案,定义一个临时变量作为媒介,实现变量值的交换。实现如下:

t = x
x = y
y = t

pythonic,对于这种需求其实python为我们提供了一种更方便的解决方案。

x, y = y, x

从代码上就可以直观的理解此处的意图,即实现x与y变量值的交换。

到这里都非常容易理解,但是接下来我们需要思考一下:此写法性能如何?为什么可以如此便捷地就是实现了变量值交换?

性能比较

虽然写法简洁方便,但是是否已损耗性能为代价呢?定义两个函数:

def swap1():
    x = 1
    y = 2
    t = x 
    x = y
    y = t

def swap2():
x = 1
y = 2
x, y = y, x

为了更好的看出性能差异,循环调用分别调用两函数100次(需要在ipython中执行):

swap1耗时38µs

%time a = [swap1() for _ in range(100)]

结果如下:

CPU times: user 31 µs, sys: 7 µs, total: 38 µs
Wall time: 67 µs

swap2耗时18µs

 %time a = [swap2() for _ in range(100)]

结果如下:

CPU times: user 18 µs, sys: 0 ns, total: 18 µs
Wall time: 21 µs

可以看出pythonic的写法比简单粗暴的引入新的辅助变量要快很多。写法如此简洁而且性能高,何乐而不为呢。

补充:这有一篇文章 python面试值交换变量值 从底层解释了两种方式性能上差异的原因。

多些思考

那么下面再思考一个问题:为什么python可以用这种写法来赋值呢?

看一些赋值运算符右边的表达式,即 y, x,这实际在python中称为元组的数据结构。我们可以看到赋值表达式左边是 x, y,那么为什么元组可以直接赋值给 x,y 呢?

此处利用了python的一个特性,即任何序列(或可迭代的对象)都可以通过简单的赋值操作分解为单独的变量。我们再来看一个例子:

name, age, mobile = 'polo', 30, '15312210823'

执行以上代码便可将name赋值为polo,age赋值为30,phone赋值为15312210823。

延伸扩展

除了以上这种简单序列的拆解,python同样支持其他更复杂的场景,下面来看看多层嵌套变量的分解,例子最直观:

school_name, (student_name, stduent_age, stduent_sex) = '致远中学', ('polo', 18, 'M')

也可以支持不定长序列的灵活分解,比如现在有一个班级已排序的学生成绩列表,如下:

scores = [21, 34, 36, 56, 60, 75, 76, 81, 83, 86, 86, 89, 90, 95, 98, 99]

我们的目标获取成绩最大、最小和其他学生的成绩列表,直接通过序列的分解便可快速得到需要的数据:

min_score, *other_scores, max_score = scores

这里引入了一种新的写法,*表达式变量 轻松分解出中间的可迭代对象并赋值给other_scores,同时将开头和结束的对象分别赋值给min_score和max_score。

看到这里感觉序列分解似乎有点类似于正则表达式的模式匹配。

总结

虽然只是小小的变量值的交换,但本质也是由需求和语言自身特性决定的。学会一些必要的技巧,将会帮助我们写出更高质量的代码。