思维导图导读
Python是一门优雅的编程语言,总是用优美的方法地简化代码、执行高效。
一、理解3个概念
1、comprehension
译作 理解、理解力、(语言学习中的)理解联系(或训练)。根据维基百科解释:在编程语言(不限于Python)中,comprehension是一种语法结构,功能是基于已有的一个数据序列经过“过滤”(满足一定条件)生成一个新的数据序列。
其本质是一种遍历方式,但它执行速度比for循环、map快35-45%。工作方式类似于for循环。
2、Python 可变、不可变:
- 不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
- 可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。
推导式操作的对象是 可变对象。
3、6种Python标准数据类型:
- 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
- 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
推导式操作的数据对象是 可变数据。
二、详述三种推导式、一种生成器表达式
1、列表推导式(list comprehension,LC)
将一个列表经过过滤后转换成另一个列表。语法:
[表达式 for 变量 in 列表] Or [表达式 for 变量 in 列表 if条件] Or
[表达式部分 列表生成部分 过滤部分(可选)] Or
表达式部分:一般是一个表达式作用一个列表的元素;或 只是接受导出的元素,没有表达式;
列表生成部分:一般是一个for循环产生初始列表,并依次导出元素;
过滤部分:一般由一个if判断构成,条件为假的过滤掉。这部分可选。
举例1:生成一个由0-9的平方组成的列表
方案1、用for循环
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
9
这创建的名为x的变量,在循环结束后仍然存在。可以用“无副作用”的方式生成一个平方值的列表,如下:方案2
>>> squares = list(map(lambda x: x**2, range(10)))
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
方案3:list comprehesion
>>> squares = [x**2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x # 也是有“副作用”的
9
其中方案3是最简洁、易读的。列表推导式由括号中的表达式+for子句(甚至嵌套列表推导式,它本质是个列表)组成,后边可以跟更多的for循环或if条件(过滤条件),结果是生成一个由表达式、for或if语句运算得出的新列表。
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x!=y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
若用for循环来做将是:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x,y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
这不符合Python优雅气质。
举例2:将一个3x4矩阵变成4x3矩阵
>>> matrix = [
... [1,2,3,4],
... [5,6,7,8],
... [9,10,11,12],
... ]
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
如果用for循环完成,将是多少行语句。LC是更简单有效的强力工具。LC总是返回一个结果,无论是否用到它。
2、生成器表达式
将列表推导式的 [] 改成 () 就得到生成器表达式。
生成器中的值只能按顺序调用一次,只能向前,不能后退。其工作方式是每次处理一个对象,而不是一次性处理和构造整个数据结构,优点是可以节省大量内存。因此,在处理大量数据时,建议使用生成器表达式,而不是列表推导式。生成器表达式结构:
生成器=(返回结构 执行对象 if 判断条件)
举例:得到30以内能被3整除的数。
>>> multiples = (i for i in range(30) if i%3 is 0)
>>> multiples
<generator object <genexpr> at 0x7f2506c4d9b0>
>>> type(multiples)
<type 'generator'>
>>> for i in multiples:
... print(i)
...
0
3
6
9
12
15
18
21
24
27
3、字典推导式
字典推导式,跟列表推导式的使用方法是类似的。只是将中括号[] 改成了 花括号{}。
举例:快速更换key、value。
>>> d = {'a':1, 'b':2}
>>> d = {v:k for k,v in d.items()}
>>> d
{1: 'a', 2: 'b'}
4、集合推导式
集合推导式,跟列表推导式也是类似的。唯一的区别是使用花括号。
集合自带去重功能,如果计算得出的元素有重复值,在集合中一个值只会录入一个元素。
>>> s = {1,2,3,4,5,6}
>>> s = {i**2 for i in s if i%2==0}
>>> print(s)
set([16, 36, 4])
总结,这些推导式语法结构上的高度相似的、可举一反三,代码简洁、优雅、高效。
参考:官方文档-List Comprehensions