2018-01-26-列表解析和生成器到底是啥?

2018-01-27  本文已影响0人  0xC00005

前言

自从上次码风被某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)之后

某南京大学的python讲座
发现自己连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 写代码得了
还是参考一下别人的说明

  1. 当需要只是执行一个循环的时候尽量使用循环而不是列表解析,这样更符合python提倡的直观性。
    for item in sequence:
    process(item)
  2. 当有内建的操作或者类型能够以更直接的方式实现的,不要使用列表解析。
    例如复制一个列表时,使用:L1=list(L)即可,不必使用:L1=[x for x in L]
  3. 当序列过长, 而每次只需要获取一个元素时,使用生成器表达式。
  4. 列表解析的性能相比要比map要好,实现相同功能的for循环效率最差(和列表解析相比差两倍)。
  5. 列表解析可以转换为 for循环或者使用map(其中可能会用到filter、lambda函数)表达式,但是列表解析更为简单明了,后者会带来更复杂和深层的嵌套。

参考资料?

python中for循环的工作原理
python 中的列表解析和生成表达式
某南京大学的金牌网课?

最后的最后

去小姐姐的官网给颗星星吧~

谢谢捧场( • ̀ω•́ )✧

上一篇下一篇

猜你喜欢

热点阅读