2018-01-26-列表解析和生成器到底是啥?
前言
自从上次码风被某hub上的人吐槽【甘拜下风】之后,我就一直在研究如何如何把代码写的更短♂小精悍
【NOI老毛病之能少打字就少打字......】
由于之前一直都是思路写注释清晰向,初次接触列表解析什么的“短代码”又闻到了一股子曾经不惜一切减复杂度的味道(说实话没人会在乎那TLE的一秒两秒的嘿嘿嘿)
正片
先用正常的风格写一个生成列表的代码
>>> for num in range(11):\
lists.append(num)
>>> lists
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
然后我们来看看可以如何删减代码,先从for循环的原理入手
For循环的原理
使用了BDFS(Baidu-FS)之后
发现自己连for都不知道是什么......
for 变量a in 另一个可迭代的变量:
执行代码
else:#当for结束之后
执行代码
什么是可迭代的对象呢?说白了就是可以被遍历的对象,像字符串,列表,字典等等,就是每一次更新之后可以改变数值的对象,类似于C里面的int,doubld和常量【忘记怎么打了】就都是不可迭代对象
就像下面这样,不可迭代对象自定义列MyRange, for是不认的
>>> class MyRange:
... def __init__(self, num):
... self.num = num
...
>>> for i in MyRange(10):
... print(i)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'MyRange' object is not iterable
所以综上所述,使用列表解析所需要的:
for,表达式,可迭代对象,条件,方括号[ORZ]
列表解析
语法
[变量a(添加条件) for 同时也是可迭代变量的a in 一个可迭代的对象b if 表达式]
1.你需要一对中/方括号【赶紧膜拜[FKH]大学神】
2.在里面有一个表达式 例如i for 一个可迭代的变量i
3.把i in到一个可迭代的对象里面
5.别忘了加上冒号:【滑稽】
加冒号会报错啊别真的加了啊喂~
如上,上面一长串的代码就可以缩成:
lists=[i for i in range(11)]#不需要事先定义Lists为空列表
达成成就:在列表/数组里面写表达式
在列表表达式里面添加条件
如果我们需要生成1~11的奇数序列呢
我猜大部分人应该都会想到.....
lists=[i for i in range(1,11,2)]#不需要事先定义Lists为空列表
是吗傻逼们?
>>> lists=[i for i in range(1,11,2)]
>>> lists
[1, 3, 5, 7, 9]
恭喜你不仅仅TLE还RE爆掉一个点
这时候请返回上面看看在列表解析式里面如何高(zhuang)雅(Bi)的写条件判断语句
#i+1是因为奇数从1开始,而列表从0生成
>>> [i+1 for i in range(11) if i%2==0]
[1, 3, 5, 7, 9, 11]
生成器解析式
请把方括号改成圆括号()
好了你学会了?
>>> (i+1 for i in range(11) if i%2==0)
<generator object <genexpr> at 0x037C0120>
列表解析和生成器语法对比
#列表解析
[expr for iter_var in iterable]
[expr for iter_var in iterable if cond_expr]
#生成器
(expr for iter_var in iterable)
(expr for iter_var in iterable if cond_expr)
得到结论:没有区别
那到底有什么区别,生成器解析的结果在哪里?
生成器表达式并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。 生成器表达式使用了“惰性计算”(lazy evaluation,也有翻译为“延迟求值”,我以为这种按需调用call by need的方式翻译为惰性更好一些),只有在检索时才被赋值( evaluated),所以在列表比较长的情况下使用内存上更有效。
结语
好了不说了代码变短了但是谁都读不懂2333
还是遵循import this 写代码得了
还是参考一下别人的说明
- 当需要只是执行一个循环的时候尽量使用循环而不是列表解析,这样更符合python提倡的直观性。
for item in sequence:
process(item)- 当有内建的操作或者类型能够以更直接的方式实现的,不要使用列表解析。
例如复制一个列表时,使用:L1=list(L)即可,不必使用:L1=[x for x in L]- 当序列过长, 而每次只需要获取一个元素时,使用生成器表达式。
- 列表解析的性能相比要比map要好,实现相同功能的for循环效率最差(和列表解析相比差两倍)。
- 列表解析可以转换为 for循环或者使用map(其中可能会用到filter、lambda函数)表达式,但是列表解析更为简单明了,后者会带来更复杂和深层的嵌套。
参考资料?
python中for循环的工作原理
python 中的列表解析和生成表达式
某南京大学的金牌网课?
最后的最后
谢谢捧场( • ̀ω•́ )✧