这一篇笔记要介绍的也是Python中独有的特性:推导式。
基本语法
在介绍推导式前,我们先来看一段编程中非常常见的代码:
string = "this is a test String"
strList = []
for char in string:
strList.append(char)
print(strList)
# ['t', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g']
这段代码通过遍历一个字符串中的字符,创建了一个存储字符的列表。
这种用途在我们编写代码时候很常见,都是通过处理一个已有容器,来生成一个新的容器。
对于这种特殊的场景,Python提供一个叫推导式的特殊表达式,可以起到等效用途:
string = "this is a test String"
strList = [char for char in string]
print(strList)
# ['t', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g']
可以看到,我们用简练的一个表达式起到了前边for
循环的效果。
推导式能做的不仅于此,我们再看一个例子:
a = (1, 2, 3, 4, 5)
b = []
for i in a:
if i % 2 == 0:
b.append(i*3)
print(b)
# [6, 12]
在这个例子中,我们会把元组a
中能被2整除的元素*3
后加入列表b
。
这其中涉及对元组的循环、筛选和处理,而所有的这一切推导式也可以做到:
a = (1, 2, 3, 4, 5)
b = [num*3 for num in a if num % 2 == 0]
print(b)
# [6, 12]
两者是等效的。
从上边可以看出,推导式的基本语法是[结果值 循环 筛选条件]
。
为什么要使用
我们现在来讨论为什么要使用推导式,简单地讲,使用推导式有以下几个好处:
-
代码简练
-
执行效率高
-
可以用于任何地方
代码简练
这点显而易见,但是我们要知道,编写代码并非越简练越好。
我现在还记得,当初学C++
的时候,条件表达式?:
就是一个如果这里的推导式一样的存在,非常简练。但是,学过之后从来未曾在实际工作中用过,并且我也没有在其它语言中追求类似条件表达式的东西,因为可读性太差,远不如老老实实的写if/else
好。所以我们不能也不因该仅仅因为一个表达式简练就义无反顾地去使用。
执行效率高
这点是解释器内部会在执行中对推导式进行优化,据说执行效率高于等效的for
循环。
关于代码执行效率,在我读书那会,还是挺重视的,算法导论的核心就是讨论时间复杂度和空间复杂度。而现在好像已经不是很重要了,许多解释型语言出现似乎也说明了这点,我们在时间换空间还是空间换时间上多出了一个选项,那就是换电脑。这相当于是在开挂。
但无论如何,我觉得优化还是挺重要的。毕竟无论CPU性能如何强劲,IDE和编译器如何优秀,也挡不住有些糟糕的代码。比如我的第一份工作,当时在维护一个公司的OA系统,一个简单的组织架构展示页面,加载居然需要1分钟,我后来查看了下,已有代码进行了大量无效的循环和赋值操作,直接拖累了运行效率,修改后只需要几秒钟就能加载。
可以用于任何地方
这是说推导式作为一个表达式,使用中比代码块更灵活。可以用于语句嵌套,可以用于赋值等等。
因为推导式拥有以上这三个优点,所以Python中会大量使用。当然,它也不是没有缺点:可读性差。但这点可以通过长时间的使用后克服。
其它的推导式
我们在上边介绍的其实是列表推导式,当然其它容器也有推导式。
对标准容器,总共有以下几种推导式:
-
列表推导式
-
字典推导式
-
集合推导式
对,没有元组推导式,这点也很容易理解:元组是不可变的列表,并不能动态生成。
我们现在来介绍字典推导式和结合推导式。
字典推导式
字典推导式和列表推导式类似,不过是用{}
标记边界,并且结果值是key/value
。
我们来看一个简单的例子:
import pprint
peopleList = [["jack chen", 16], ["brus lee", 20]]
peopleDict = {}
for person in peopleList:
peopleDict[person[0]] = person[1]
pprint.pprint(peopleDict)
# {'brus lee': 20, 'jack chen': 16}
我们用推导式来取代:
import pprint
peopleList = [["jack chen", 16], ["brus lee", 20]]
peopleDict = {person[0]: person[1] for person in peopleList}
pprint.pprint(peopleDict)
# {'brus lee': 20, 'jack chen': 16}
可以看到我们用:
来分隔key
和value
。
集合推导式
集合推导式与字典推导式类似,同样以{}
标记边界,我们来看这个例子:
import pprint
peopleList = [["jack chen", 16], ["brus lee", 20]]
peopleSet = set()
for person in peopleList:
peopleSet.add(person[0])
pprint.pprint(peopleSet)
# {'brus lee', 'jack chen'}
用推导式替换:
import pprint
peopleList = [["jack chen", 16], ["brus lee", 20]]
peopleSet = {person[0] for person in peopleList}
pprint.pprint(peopleSet)
# {'jack chen', 'brus lee'}
集合推导式和字典推导式的区别就是没有:
。
现在我们已经知道了,用[]
包裹的是列表推导式,用{}
包裹的,有:
的是字典推导式,没有的是集合推导式。而元组推导式并不存在,那是不是就没有用()
包裹的类似表达式呢?
关于生成器的内容我就放在下一篇笔记了,谢谢阅读。
本系列文章的代码都存放在Github项目:
文章评论