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循环无法实现的时候,还可以用函数来实现。