简书不维护了,欢迎关注我的知乎:波罗学的个人主页
知乎地址: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。
看到这里感觉序列分解似乎有点类似于正则表达式的模式匹配。
总结
虽然只是小小的变量值的交换,但本质也是由需求和语言自身特性决定的。学会一些必要的技巧,将会帮助我们写出更高质量的代码。