其他分享
首页 > 其他分享> > 20210514 列表生成式与生成器

20210514 列表生成式与生成器

作者:互联网

Example A:
Requirement:
[1, 2, 3] --> [2, 4, 6]
Solution:
def maps(a):
    return [i*2 for i in a]
1-1 列表生成式
>>> a = [1,2,3]
>>> a
[1, 2, 3]
>>>
>>> [i*2 for i in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# for i in range(10) 这是一个循环,循环 10 次,i*2 中的 i 就是 for 循环中的 i
# 把 for 循环中的临时变量 i 赋值到前面的 i*2 的 i 中,得到的结果,就是列表中的元素
# 这个列表 和 列表 a 的区别是:列表 a 是写死的,下面的 i*2 是可以进行操作的
1-1-1
# [i*2 for i in range(10)] 相当于
>>> a = []
>>> for i in range(10):
...     a.append(i*2)
...
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# 相当于至少 3 行代码; [i*2 for i in range(10)] 就叫 列表生成式
列表生成式的主要作用就是使代码更简洁
1-1-2
当然列表生成式也可以传函数,比如
>>> [func(i) for i in range(10)]
注:
# 在 python2 上 range(10) 数据已经准备好了
# 在 python3 上 range(10) 是没有准备好的
Example B:
Given a string of digits, you should replace any digit below 5 with '0' and any digit 5 and above with '1'. 
Return the resulting string.
Solution A:
def fake_bin(x):
    return ''.join['0' if int(a) < 5 else '1' for a in x]
Solution B:
def fake_bin(x):
    return ''.join('0' if int(a) < 5 else '1' for a in x)
# 为什么Solution A 是[],而Solution B 中是 ()?
2-1 生成器
通过列表生成式,我们可以直接创理一个列表。
但是,受到内存限制,列表容量肯定是有限的。
如果仅需要访问前面几个元素,后面绝大多数元素占用的空间都白白浪费了。
所以, 如果列表元素可以按照某种算法推算出来
是否可以在循环的过程中不断推算出后续的元素呢?
这样就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator, 有很多种方法。
第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator;
# 比如希望在循环的时候,循环到 列表的 第 4 个元素时,这个元素才刚生成;
# 这样,就不用把所有数据都提前准备好了,就可以节省空间
2-1-1
>>> [i*2 for i in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> (i*2 for i in range(10))
<generator object <genexpr> at 0x0000027B0E6C3BC8>
# 这就变成了一个生成器,对这个数据进行循环时,每循环一次就按此规律乘 2
2-1-2
>>> b = (i*2 for i in range(10))
>>> for i in b:
...     print(i)
0
2
4
……
18
>>>
# 这里的数据量比较少,所以也许无法看出差异;
# 但如果数据量达到100个以上,这种方法可以马上返回数据,这时,如果继续用以往的形式,就会感觉到慢了
2-1-3
>>> c = ( i*2 for i in range(100000000))
>>> c
<generator object <genexpr> at 0x0000027B0E6F72C8>
>>>
# 事实上,这里并没有生成数据,只是返回了一个地址,除非访问这个地址,才会生成数据
# 如果不访问,这个数据就不会存在;
# 列表则是把所有数据都准备好的;
# 而生成器则是准备了一个算法,算法写好了一个规则,调用时,才会产生数据
2-1-4
# 现在想访问 c 的第 1000 个数据,前面的 999 个数据要被循环到,才能够调用;
# 否则无法直接一下调用第 1000 个
>>> c[1000]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not subscriptable
# 所以它不支持列表的那种切片;既然生成器不支持切片,那生成器能够支持什么呢?
# 除了用 for 循环一个一个的取,还支持别的方式吗?
# 没有,只能用 for 循环这一种方式获取
2-1-5
# 如果只访问 2-1-3 中的前两个数据,应该怎么做呢?
# 生成器,有一个方法,叫做 __next__() 方法
>>> c = ( i*2 for i in range(100000000))
>>> for i in c:
...     print(i)
...
0
2
4
……
2210
2212
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt
>>> c.__next__()
2214
>>> c.__next__()
2216
>>> c.__next__()
2218
>>> c.__next__()
2220
>>>
# 现在要回去取 2210,是不可以的,回去取是不行的;而且也没有任何相应的方法让你回到上一个
# 生成器只记住当前位置,它不知道前面,也不知道后面;前面用完了,就没了
# 所以既不能往前走,也不能往后退;只能一点一点的往后走
1 生成器 只有在调用时才会生成相应的数据
2 只记录当前位置
3 只有一个 __next__()方法。(在 python2.7 中是 next())

标签:__,10,生成式,生成器,列表,range,20210514
来源: https://blog.51cto.com/u_15149862/2777678