1,迭代器协议:对象必须具有一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopInteration异常,以来终止迭代(只能往后走,不能向前进)
2,可迭代对象:实现迭代器协议的对象(再对象的内部定义一个__iter__()方法)
问题:for循环的本质就是去遵循迭代器协议去访问对象,for循环可以遍历(字符串,列表,元组,字典,集合,文件对象),那么这些数据类型肯定也是可迭代对象?但定义一个列表L = [1,2,3],中没有L.next方法
解答:(字符串,列表,元组,字典,集合,文件对象)都不是可迭代对象,只是在进行for循环时,调用了它们内部的__iter__方法,将它们编程可迭代对象
# __iter__() __next__() 方法
test = "hello" iter_test = test.__iter__() # 再字符串test的内部具有__iter__方法 print(iter_test) # 一个可迭代对象<str_iterator object at 0x00000226F70325F8> print(iter_test.__next__()) # 具有 __next__() 方法,输出 h
# for循环与__iter__()方法
l = [1,2,3,4,5] for i in l: # 这一步相当执行了 l.__iter__() 将其转为可迭代对象 print(i) # 输出 1 2 3 4 5 ## 注:因为可迭代对象在没有下一个子元素时,会引起StopIteration异常,但在进行for循环是,循结束时不会引起异常, ## 因为for循环捕捉到了该异常,停止再次进行循环 # 注:非序列类型不能利用while进行遍历 字典,集合,文件对象(就是无下标),可以利用for循环来遍历 # for 循环就是基于迭代器提供了一个统一的可以遍历所有对象的方法,在遍历之前需要调用对象的__iter__方法将其转换成一个迭代器
# 用可迭代的方式除去列表中元素的值
l_list = [1,2,3] list_iter = l_list.__iter__() # 将其变为可迭代对象 print(list_iter.__next__()) # 取得第一个对象1 print(list_iter.__next__()) # 取得第二个对象2 print(list_iter.__next__()) # 取得第三个对象3 # print(list_iter.__next__()) # 会有报错
# 内置函数 next()用法
l = [1,2,3,4,5] iter_l = l.__iter__() print(next(iter_l)) # 输出第一个值1 # next()方法为内置函数,调用next()相当于iter_1.__next__()
。。。
生成器:可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其它的数据类型需要调用__iter__() 方法),所以生成器为可迭代对象
## Python中的生成器
1.生成器函数:定义一个普通的函数,但是函数不利用return返回结果,而是利用 yield 语句来返回结果
2.yield 一次只返回一个结果,在每个结果的中间,挂起函数的状态,以便下次执行
3.生成器表达式:与列表的推导类似,但是,生成器按需产生结果的一个对象,而不是一次构建一个结果列表
4.生成器的优点:使用生成器对延迟操作提供支持,所谓的延操作指再需要的时候才产生结果,而不是立即产生结果
# 生成器小结:
1,可迭代对象
2,实现延迟计算,节省内存
3,生成器和其它的数据类型一样,都是实现了迭代器协议,但生成器附加了一个延迟计算省内存的,其它的可迭代对象没有
## 生成器函数
def test(): yield 1 # 返回一个值 可以填入各种形式的值 yield 2 yield 3 g = test() # 获得生成器,但生成器未运行 print(g) # <generator object test at 0x0000025BB0032480> print(g.__next__()) # 输出:1 执行生成器 print(g.__next__()) # 输出:2 print(g.__next__()) # 输出:3 当返回多个值是这样则不会报错 print(g.__next__()) # 报错 抛出异常:StopIteration
# 生成器只能遍历一次
def test(): for i in range(4) yield i t = test() # 获得生成器,但生成器未运行 for i in t: print(i) # 对生成器进行迭代 for i in t: print(i) # 输出为空,因为生成器最多只能迭代一次
# 生成器例子
# 员工工资表.txt {"position":"老板","salary":"1"} {"position":"秘书","salary":"10000"} {"position":"文员","salary":"10000"} {"position":"工程师","salary":"100000"} {"position":"经理","salary":"10000"} def get_salary(filename): with open(filename) as f: for line in f: s = eval(line) yield s["salary"] res = get_salary("员工工资表.txt") all_salary = sum(res) for i in res: print(p) # 当执行for循环时,将不会有任何的输出,因为生成器只执行一次,在执行sum语句时。就遍历了生成器,当在for循环语句时,再次遍历生成器时,将不会取到任何值
....
name = "henry" res = "hello" if name == "henry" else "hi" res = print("名字为henry") if name == "henry" else print("名字不为henry") # 当名字为henry时 执行"hello" # 当名字不为henry时 执行hi # res为取得的返回值
....
hot_spicy_pot = [] for i in range(10): hot_spicy_pot.append("吃%s份麻辣香锅" %i) print(hot_spicy_pot) # ['吃0份麻辣香锅', '吃1份麻辣香锅', '吃2份麻辣香锅', '吃3份麻辣香锅', '吃4份麻辣香锅', '吃5份麻辣香锅', '吃6份麻辣香锅', '吃7份麻辣香锅', '吃8份麻辣香锅', '吃9份麻辣香锅'] # 三元表达式 hot_spicy_pot = [] [hot_spicy_pot.append("吃%s份麻辣香锅" %i) for i in range(10)] print(hot_spicy_pot) # ['吃0份麻辣香锅', '吃1份麻辣香锅', '吃2份麻辣香锅', '吃3份麻辣香锅', '吃4份麻辣香锅', '吃5份麻辣香锅', '吃6份麻辣香锅', '吃7份麻辣香锅', '吃8份麻辣香锅', '吃9份麻辣香锅'] hot_spicy_pot = [] [hot_spicy_pot.append("吃%s份麻辣香锅" %i) for i in range(10) if i > 5] print(hot_spicy_pot) # ['吃6份麻辣香锅', '吃7份麻辣香锅', '吃8份麻辣香锅', '吃9份麻辣香锅']
# 如果当处理的数据较大时,若将所处理的数据全部放于内存中(例如构建一个较大的列表时),会比较占内存
### 所以就有了生成器表达式
# 生成器表达式(将列表解析中的中括号改成小括号)
列表解析:["吃%s份麻辣香锅" %i for i in range(10) ] 生成器表达式:("吃%s份麻辣香锅" %i for i in range(10))
# 列表解析 hot_spicy_pot = [] res = [hot_spicy_pot.append("吃%s份麻辣香锅" %i) for i in range(10)] print(hot_spicy_pot) print(res) # 无意义 [None, None, None, None, None, None, None, None, None, None] # 生成器表达式 hot_spicy_pot = [] res = ("吃%s份麻辣香锅" %i for i in range(10)) print(hot_spicy_pot) # [] print(res) # <generator object <genexpr> at 0x0000018945032480> 可迭代对象 print(res.__next__()) # 吃0份麻辣香锅 print(next(res)) # 吃1份麻辣香锅
# 总结:
1.列表解析和生成器表达式都是一种遍历的编程方式,但生成器表达式更节省内存
# 例:当计算一个较大的列表时,需要将其先加载到内存中,所以会占用较大的内存,可以用下列的方法解决
sun(i for i in range(10000000000000000000))
。。。。
# send(self,value)方法 def test(): print("开始啦") first = yield 1 print("第一次",first) # 输出的为 None yield 2 print("第二次") t = test() res = t.__next__() t.send(None) # value可以为任何值 # 注:执行完上一次的__next__(),当前的执行位置位于yield后,当执行send(None)时,将None的值赋值给first ##注:yield相当于return控制的函数的返回值 # x = yield的另一个特性,接收传过来的值,赋值给x
# 吃包子模拟单线程
def consumer(name): print("我是[%s],我准备开始吃包子了"%name) while True: baozi = yield print("%s很开心的把{%s}吃掉了"%(name,baozi)) def producer(): c1 = consumer("henry") c1.__next__() for i in range(10) c1.send("韭菜包子馅")
。。。。