标签:data 异常 运行 出现 需要 code 是的 时间 迭代器协议
一般来说,生成器函数和常规函数一样,并且实际上也是用常规的def语句编写的。然而,当创建时,他自动实现迭代协议,以便可以出现在迭代背景中。
0.迭代协议
有__next__方法的对象会前进到下一个结果,当到达结尾时则会引发StopIteration异常。在python中,任何这类对象都认为是可迭代的。任何这类对象也能以for循环或其他迭代工具遍历,因为所有迭代工具内部工作起来都在每次迭代中调用__next__,并且捕捉StopIteration异常来确定何时离开。
# 读取文本文件的最佳方式就是根本不要去读取;代替的办法是让for循环在每轮自动调用next从而前进到下一行 # 这是读取文本文件的最佳方式,因为写法最简单,运行最快,并且从内存使用情况来说也是最好的。 # 这个和for line in f.readlines是一个效果 l =list() with open(‘../data/123.txt‘) as f: # a = f.readline() for line in f: print(line, end=‘‘) # end=‘‘是为了抑制一个\n 因为已经有一个\n了 l.append(line) print(l)
1.手动迭代:iter和next
python3提供了一个内置函数next,它会自动调用一个对象的__next__方法。给定一个可迭代对象X,调用next(X)等同于X.__next__()。
手动迭代的方法就是先创建一个迭代器(iter),然后通过迭代器的next方法进行迭代。具体做法如下:
D = {‘a‘:‘A‘,‘b‘:‘B‘,‘c‘:‘C‘} I = iter(D) print(next(I)) # a print(next(I)) # b
但是对于文件来说,是不需要初始化迭代器的(上面代码第二行),因为文件对象就是自己的迭代器。也就是说,文件有自己的__next__方法,因此不需要像这样返回一个不同的对象。列表以及很多其他的内置对象,不是自身的迭代器,因为它们支持多次打开迭代器。对这样的对象,我们必须调用iter来启动迭代,他们(dict, list)没有自己的__next__方法:
D = {‘a‘:‘A‘,‘b‘:‘B‘,‘c‘:‘C‘} I = iter(D) if iter(D) is D: print(‘true‘) elif iter(D) is I: print(‘I is iter‘) # 我本来以为会打印这一句,结果不是的,不知道为什么 else: print(‘dict dont have __next__ func‘) # print this one f = open(‘../data/123.txt‘) print(f.__next__()) if iter(f) is f: print(‘TRUE‘) # print this one
2.列表解析
例如以下代码就是一个常见的列表解析:
L = range(10) L = [x + 10 for x in L] # [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
在文件中readlines方法可以一次性地吧文件载入到行字符串的一个列表中,这个有效但是结尾会含有一个换行符。去掉换行符,并保存到一个列表的写法如下:
lines = [line.rsplit() for line in open(‘123.txt‘)] # rsplit反向去掉空格(换行符)
增加测试和嵌套循环的列表解析:
[x for x in range(5) if x %2 ==0] # [0, 2, 4] list(filter((lambda x: x % 2 ==0), range(5))) # [0, 2, 4]
再复杂点的:
list(map((lambda x: x**2), filter((lambda x: x %2 ==0), range(10)))) # [0, 4, 16, 36, 64]
矩阵中使用:
M = [[1,2,3], [4,5,6], [7,8,9]] [row[1] for row in M] # [2,5,8]
3.理解解析列表
优点:map调用比等效的for循环要快两倍,而列表解析往往比map调用要稍快一些。速度上的差距是来自于底层实现上,map和解析列表是在解释器中以C语言的速度来运行的,比python的for循环化代码在PVM中步进运行要快的多。
缺点:解析列表和map没有for循环逻辑清晰。
4.生成器
生成器函数:编写为常规的def语句,但是使用yield语句一次返回一个结果,在每个结果之间挂起和继续它们的状态。
生成器表达式类似于列表解析,但是,它们返回按需产生结果的一个对象,而不是构建一个结果列表,从语法上讲,生成器表达式就像一般的列表解析一样,但是它们是括在圆括号中而不是方括号中的。
由于二者都不会一次性构建一个列表,它们节省了内存空间,并且允许计算时间分散到各个结果请求。
4.1生成器函数: yield VS return
状态挂起
和返回一个值并退出的常规函数不同,生成器喊自动在生成值的时刻挂起并继续函数的执行。
生成器函数和常规函数之间的主要的代码不同之处在于,生成器yields一个值,而不是return一个值。yield语句挂起该函数并向调用者发送回一个值(return 则直接返回结果并结束了这个func),但是,保留足够的状态以使得函数能够从它离开的地方继续。当继续时,函数在上一个yield返回后立即继续执行。
迭代协议整合
迭代协议:可迭代的对象定义了一个__next__方法,它要么返回迭代中的下一项,或者引发一个特殊的stopIteration异常来终止迭代。一个对象的迭代器用iter内置函数接受。
实现方法:如果函数中包含了yield 语句(注意是语句不是函数),该语句则会编制为生成器。即自动实现迭代器协议。当调用生成器的时候,它返回一个迭代器对象,该对象支持用一个名为__next__的自动创建的方法来继续执行的接口。生成器喊也可能有一条return语句,总是在def语句块的末尾,直接终止值的生成。从技术上讲,可以在任何常规函数退出执行以后,引发一个stopIteration异常来实现。
生成器函数应用
def a(n): for i in range(n): yield i ** 2 for i in a(5): print(i, end=‘:‘) # 0:1:4:9:16: x = a(5) print(type(x)) # generator print(x.__next__()) # 0 print(next(x)) # 1 print(next(x)) # 4
扩展生成器函数协议:send和next
再更新
标签:data 异常 运行 出现 需要 code 是的 时间 迭代器协议
原文地址:https://www.cnblogs.com/SsoZhNO-1/p/11448118.html