标签:捕捉异常 lis 算法 判断 产生 计算 推算规则 __next__ pytho
现在有这么一个需求,要将[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这个列表中的每个元素都加1,那么需要怎么实现呢?你可能会想到这几种方式:
a = list(range(0,10))
# print(a)
# 1.二逼青年版
b = []
for i in a:
b.append(i+1)
print(b)
# 2.普通青年版
c = a
for index,i in enumerate(c):
a[index] += 1
print(c)
# 3.文艺青年版
d = a
d = map(lambda x:x,d)
for i in d:
print(i)
其实还有一种写法,如下:
# 4.装逼青年版
e = a
e = [i+1 for i in range(10)]
print(e)
通过列表生成式,我们可以直接创建一个列表。但是受到内存限制,列表容量肯定是有限制的,就像是递归,最大递归深度python就对其作了限制。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅想访问的是前几个元素的话,那么后面绝大多数元素占用的空间就是浪费了。
就比如说:我们家是卖手机的,我们找到了专门制作手机的厂商,告诉他,我这个手机要1000万台,你们给我做吧,然而厂商考虑,和你说:这款手机我不知道能不能卖这么多,你看我们就1000台分一批给你生产出来,如果你还要,那么再给你生产1000台,你考虑到这也是一个问题,所以便同意了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环中不断推算出后面的元素呢?这样就不需要去创建一个完整的list了,从而节省了大量的空间,在python中,这样一边循环一边计算的机制,称为:生成器:generator
创建一个generator有很多种办法,第一种方法很简单,只需要把一个列表生成式的[]改成(),就创建了一个generator:
l = [x*x for x in range(10)]
print(l) # l = [x*x for x in range(10)]
g = (x*x for x in range(10))
print(g)
运行结果为:
<generator object <genexpr> at 0x000001E28508FFC0>
创建l和g的却别仅在于最外层的[]和(),l是一个list,而g是一个generator
我们可以直接打印出来l列表中的每一个元素,但是怎么打印出generator的每一个元素呢?
如果需要打印的话,我们就需要使用next()函数获得generator的下一个返回值:
g = (x*x for x in range(10))
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
运行结果为:
<generator object <genexpr> at 0x000002586EF82518>
0
1
4
9
16
我们讲过,generator保存的是算法,每次调用next(g)后就能计算出g的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,会抛出StopIteration异常
当然,每需要获取下一个元素的值,就需要使用一次next的话,就有点太变态了,所以,正确的方法时for循环,因为generator也是可迭代的
g = (x*x for x in range(10))
for i in g:
print(i)
运行结果为:
0
1
4
9
16
25
36
49
64
81
genera足够强大,如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现,著名的斐波那契数列用列表生成式写不出来,但是用函数把它打印出来却很容易:
def fib(max):
n,a,b = 0,1,1 # 分别赋值
while n < max: # 条件判断
print(b)
a,b = b,a+b # 赋值,把b赋值给a,把a+b赋值给b,在这里需要注意的是,是相加之前的结果,不是把b赋值给a后,然后再相加
n += 1
return ‘Done‘
fib(10)
运行结果如下:
1
2
3
5
8
13
21
34
55
89
仔细观察:可以看出,fib函数是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator只有一步之遥。要把fib函数编程生成器generator,只需要把print(b)改成yield b就可以了:
def fib(max):
n,a,b = 0,1,1
while n < max:
yield b
# 出现了yield就相当于把这段程序变成了生成器
# yield把值返回到外部,而且不用终止
# yield把函数的执行过程冻结到这一步,并且把b的值返回给外面的next()
a,b = b,a+b
n += 1
return ‘Done‘
f = fib(10)
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
运行结果如下
1
2
3
5
8
13
想要获取下一个元素的值,用next就可以了
这里最难理解的是generator和函数的执行顺序流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回(结束),而编程generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次被next()调用时从上次返回的yield语句出继续执行。
def fib(max):
n,a,b = 0,1,1
while n < max:
yield b
a,b = b,a+b
n += 1
return ‘Done‘
data = fib(10)
print(data)
print(data.__next__())
print(‘干点别的事‘)
print(data.__next__())
运行结果如下:
<generator object fib at 0x0000020D5B02FFC0>
1
干点别的事
2
在上面的fib例子中,我们在循环过程中不断调用yield,就会不断中断,当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们都是使用for循环来获取返回值
def fib(max):
n,a,b = 0,1,1
while n < max:
yield b
a,b = b,a+b
n += 1
return ‘Done‘
data = fib(10)
for i in data:
print(i)
运行结果如下:
1
2
3
5
8
13
21
34
55
89
但是我们发现了,如果使用for循环的话,根本拿不到generator的return语句返回值。所以如果想拿到返回的值的话,必须要捕捉异常
def fib(max):
n,a,b = 0,1,1
while n < max:
yield b
a,b = b,a+b
n += 1
return ‘Done‘
data = fib(10)
while True:
try:
x = next(data)
print(‘data:‘,x)
except StopIteration as e:
print(‘Generator return value:‘,e.value)
break
运行结果:
data: 1
data: 2
data: 3
data: 5
data: 8
data: 13
data: 21
data: 34
data: 55
data: 89
我们已经直到,可以直接作用域for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等
另一类是generator,包括生成器和带yield 的 generator fgunction等
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
可以使用isinstance()判断一个对象是否是Iterstance对象:
In [1]: from collections import Iterable
In [2]: isinstance([],Iterable)
In [2]: isinstance([],Iterable)
Out[2]: True
In [3]: isinstance((),Iterable)
Out[3]: True
In [4]: isinstance({},Iterable)
Out[4]: True
In [6]: isinstance(‘abc‘,Iterable)
Out[6]: True
In [7]: isinstance((x for x in range(10)),Iterable)
Out[7]: True
而生成器不但可以作用域for循环,还可以被next()函数不断调用并返回下一个值,直到抛出错误
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
可以使用isinstance()判断一个对象是否是Iterator对象
In [1]: from collections import Iterator
In [2]: isinstance((),Iterator)
Out[2]: False
In [3]: isinstance([],Iterator)
Out[3]: False
In [4]: isinstance({},Iterator)
Out[4]: False
In [5]: isinstance((x for x in range(10)),Iterator)
Out[5]: True
生成器都是迭代器,但列表、元组、字典虽然是可迭代对象,但不是迭代器
把列表、元组、字典等可迭代对象转换成迭代器可以使用iter()函数
In [6]: isinstance(iter([]),Iterator)
Out[6]: True
In [7]: isinstance(iter({}),Iterator)
Out[7]: True
你可能会问,为什么List,tuole,dict等数据类型不是迭代器呢?
这是因为python的迭代器对象表示的是一个数据流,迭代器对象可以被next()函数调用并不断返回下一个数据,直到没有数据的时候抛出异常。可以把这个数据流看作是一个有序序列,但我们却不能提高知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以,Iterator的计算是惰性的,只需在返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,而list时永远不可能存储全体自然数的。
标签:捕捉异常 lis 算法 判断 产生 计算 推算规则 __next__ pytho
原文地址:https://www.cnblogs.com/xiaoyafei/p/9074340.html