其他分享
首页 > 其他分享> > 20210602 生成器

20210602 生成器

作者:互联网

Example:
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 中是 ()?

1-1 生成器
通过列表生成式,我们可以直接创理一个列表。但是,受到内存限制,列表容量肯定是有限的。如果仅需要访问前面几个元素,后面绝大多数元素占用的空间都白白浪费了。
所以, 如果列表元素可以按照某种算法推算出来,是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator, 有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator;
# 比如希望在循环的时候,循环到 列表的 第 4 个元素时,这个元素才刚生成;
# 这样,就不用把所有数据都提前准备好了,就可以节省空间

1-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
1-1-2
>>> b = (i*2 for i in range(10))
>>> for i in b:
...     print(i)
0
2
4
……
18
>>>
# 这里的数据量比较少,所以也许无法看出差异;
# 但如果数据量达到100个以上,这种方法可以马上返回数据,这时,如果继续用以往的形式,就会感觉到慢了
1-1-3
>>> c = ( i*2 for i in range(100000000))
>>> c
<generator object <genexpr> at 0x0000027B0E6F72C8>
>>>
# 事实上,这里并没有生成数据,只是返回了一个地址,除非访问这个地址,才会生成数据
# 如果不访问,这个数据就不会存在;
# 列表则是把所有数据都准备好的;
# 而生成器则是准备了一个算法,算法写好了一个规则,调用时,才会产生数据
1-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 循环这一种方式获取
1-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 join()和生成器
In [84]: c = (str(i*2) for i in range(10))
In [85]: c
Out[85]: <generator object <genexpr> at 0x00000208D89C7890>
In [86]: "".join(c)
Out[86]: '024681012141618'
# 这里 join 做的事情和 for  循环是一样的,其实是相当于执行了 for 循环

1 生成器 只有在调用时才会生成相应的数据
2 只记录当前位置
3 只有一个 __next__()方法。(在 python2.7 中是 next())

# 所以,我们创建了一个generator后,基本上永远不会调用next(),
# 而是通过for循环来迭代它,并且不需要关心StopIteration的错误:
# generator非常强大。
# 如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。


标签:__,generator,生成器,列表,循环,next,20210602
来源: https://blog.51cto.com/u_15149862/2844458