day13
Posted caisong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了day13相关的知识,希望对你有一定的参考价值。
一. 迭代器
我们之前一直在用可迭代对象进行迭代操作. 那么到底什么是可迭代对象. 本小节主要讨
论可迭代对象. 首先我们先回顾一下目前我们所熟知的可迭代对象有哪些:
str, list, tuple, dict, set. 那为什么我们可以称他们为可迭代对象呢? 因为他们都遵循了可
迭代协议. 什么是可迭代协议. 首先我们先看一段错误代码:
# 对的
s = "abc"
for c in s:
print(c)
# 错的
for i in 123:
print(i)
结果:
Traceback (most recent call last):
File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 8, in
<module>
for i in 123:
TypeError: ‘int‘ object is not iterable
注意看报错信息中有这样一句话. ‘int‘ object is not iterable . 翻译过来就是整数类型对象
是不可迭代的. iterable表示可迭代的. 表示可迭代协议. 那么如何进行验证你的数据类型是否
符合可迭代协议. 我们可以通过dir函数来查看类中定义好的所有方法.
s = "我的哈哈哈"
print(dir(s)) # 可以打印对象中的方法和函数
print(dir(str)) # 也可以打印类中声明的方法和函数
在打印结果中. 寻找__iter__ 如果能找到. 那么这个类的对象就是一个可迭代对象.
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘,
‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘,
‘__getitem__‘, ‘__getnewargs__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘,
‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mod__‘,
‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘,
‘__rmod__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘,
‘__subclasshook__‘, ‘capitalize‘, ‘casefold‘, ‘center‘, ‘count‘, ‘encode‘,
‘endswith‘, ‘expandtabs‘, ‘find‘, ‘format‘, ‘format_map‘, ‘index‘,
‘isalnum‘, ‘isalpha‘, ‘isdecimal‘, ‘isdigit‘, ‘isidentifier‘, ‘islower‘,
‘isnumeric‘, ‘isprintable‘, ‘isspace‘, ‘istitle‘, ‘isupper‘, ‘join‘,
‘ljust‘, ‘lower‘, ‘lstrip‘, ‘maketrans‘, ‘partition‘, ‘replace‘, ‘rfind‘,
‘rindex‘, ‘rjust‘, ‘rpartition‘, ‘rsplit‘, ‘rstrip‘, ‘split‘, ‘splitlines‘,
‘startswith‘, ‘strip‘, ‘swapcase‘, ‘title‘, ‘translate‘, ‘upper‘, ‘zfill‘]
我们发现在字符串中可以找到__iter__. 继续看一下list, tuple, dict, set
print(dir(tuple))
print(dir(list))
print(dir(open("护?士少妇嫩模.txt"))) # ?文件对象
print(dir(set))
print(dir(dict))
结果:
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘,
‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘,
‘__getitem__‘, ‘__getnewargs__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘,
‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘,
‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__rmul__‘,
‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘count‘,
‘index‘]
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘,
‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘,
‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__iadd__‘, ‘__imul__‘, ‘__init__‘,
‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘,
‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘,
‘__reversed__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘,
‘__str__‘, ‘__subclasshook__‘, ‘append‘, ‘clear‘, ‘copy‘, ‘count‘,
‘extend‘, ‘index‘, ‘insert‘, ‘pop‘, ‘remove‘, ‘reverse‘, ‘sort‘]
[‘_CHUNK_SIZE‘, ‘__class__‘, ‘__del__‘, ‘__delattr__‘, ‘__dict__‘,
‘__dir__‘, ‘__doc__‘, ‘__enter__‘, ‘__eq__‘, ‘__exit__‘, ‘__format__‘,
‘__ge__‘, ‘__getattribute__‘, ‘__getstate__‘, ‘__gt__‘, ‘__hash__‘,
‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__lt__‘, ‘__ne__‘,
‘__new__‘, ‘__next__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘,
‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘_checkClosed‘,
‘_checkReadable‘, ‘_checkSeekable‘, ‘_checkWritable‘, ‘_finalizing‘,
‘buffer‘, ‘close‘, ‘closed‘, ‘detach‘, ‘encoding‘, ‘errors‘, ‘fileno‘,
‘flush‘, ‘isatty‘, ‘line_buffering‘, ‘mode‘, ‘name‘, ‘newlines‘, ‘read‘,
‘readable‘, ‘readline‘, ‘readlines‘, ‘seek‘, ‘seekable‘, ‘tell‘,
‘truncate‘, ‘writable‘, ‘write‘, ‘writelines‘]
[‘__and__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘,
‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘,
‘__hash__‘, ‘__iand__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__ior__‘,
‘__isub__‘, ‘__iter__‘, ‘__ixor__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘,
‘__ne__‘, ‘__new__‘, ‘__or__‘, ‘__rand__‘, ‘__reduce__‘, ‘__reduce_ex__‘,
‘__repr__‘, ‘__ror__‘, ‘__rsub__‘, ‘__rxor__‘, ‘__setattr__‘, ‘__sizeof__‘,
‘__str__‘, ‘__sub__‘, ‘__subclasshook__‘, ‘__xor__‘, ‘add‘, ‘clear‘,
‘copy‘, ‘difference‘, ‘difference_update‘, ‘discard‘, ‘intersection‘,
‘intersection_update‘, ‘isdisjoint‘, ‘issubset‘, ‘issuperset‘, ‘pop‘,
‘remove‘, ‘symmetric_difference‘, ‘symmetric_difference_update‘, ‘union‘,
‘update‘]
[‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘,
‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘,
‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘,
‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘,
‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__setitem__‘,
‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘clear‘, ‘copy‘, ‘fromkeys‘,
‘get‘, ‘items‘, ‘keys‘, ‘pop‘, ‘popitem‘, ‘setdefault‘, ‘update‘, ‘values‘]
我们发现这几个可以进行for循环的东西都有__iter__函数, 包括range也有. 可以自己试一
下.
综上. 我们可以确定. 如果对象中有__iter__函数. 那么我们认为这个对象遵守了可迭代协议.
就可以进行迭代. 这里的__iter__是帮助我们获取到对象的迭代器. 我们使用__next__()来获取
到一个迭代器中的元素. 那么我们之前讲的for的工作原理到底是什什么? 继续看代码
s = "我爱北京天安门"
c = s.__iter__() # 获取迭代器?
print(c.__next__()) # 使用迭代?进行迭代. 获取一个元素 我
print(c.__next__()) # 爱
print(c.__next__()) # 北
print(c.__next__()) # 京
print(c.__next__()) # 天
print(c.__next__()) # 安
print(c.__next__()) # 门
print(c.__next__()) # StopIteration
我们可以把要迭代的内容当成子弹. 然后呢. 获取到迭代器__iter__(), 就把子弹都装在弹夹
中. 然后发射就是__next__()把每一个子弹(元素)打出来. 也就是说, for循环的时候. 一开始的
时候是__iter__()来获取迭代器. 后面每次获取元素都是通过__next__()来完成的. 当程序遇到
StopIteration将结束循环.
二. 生成器
什么是生成器. 生成器实质就是迭代器.
在python中有三种方式来获取生成器:
1. 通过生成器函数
2. 通过各种推导式来实现生成器
3. 通过数据的转换也可以获取生成器
今天, 我们主要使用生成器函数来完成生成器的创建和使用.
首先, 我们先看一个很简单的函数:
def func():
print("111")
return 222
ret = func()
print(ret)
结果:
111
222
将函数中的return换成yield就是生成器
def func():
print("111")
yield 222
ret = func()
print(ret)
结果:
<generator object func at 0x10567ff68>
运行的结果和上面不一样. 为什么呢. 由于函数中存在了yield. 那么这个函数就是一个生成器
函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了. 而是获取这个生成器.
如何使用呢? 想想迭代器. 生成器的本质是迭代器. 所以. 我们可以直接执行__next__()来执行
以下生成器.
def func():
print("111")
yield 222
gener = func() # 这个时候函数不会执行. 而是获取到生成器?
ret = gener.__next__() # 这个时候函数才会执行. yield的作用和return一样. 也是返回
数据
print(ret)
结果:
111
222
那么我们可以看到, yield和return的效果是一样的. 有什么区别呢? yield是分段来执行一个
函数. return呢? 直接停止执行函数.
def func():
print("111")
yield 222
print("333")
yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__() # 最后一个yield执行完毕. 再次__next__()程序报错, 也就是
说. 和return无关了.
print(ret3)
结果:
111
Traceback (most recent call last):
222
333
File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in
<module>
444
ret3 = gener.__next__() # 最后一个yield执行完毕. 再次__next__()程序报错, 也就是说. 和return无关了了.
StopIteration
当程序运行完最后一个yield. 那么后面继续进行__next__()程序会报错.
好了生成器说完了. 生成器有什么作用呢? 我们来看这样一个需求. 老男孩向JACK JONES订
购10000套学生服. JACK JONES就比较实在. 直接造出来10000套衣服.
def cloth():
lst = []
for i in range(0, 10000):
lst.append("衣服"+str(i))
return lst
cl = cloth()
但是呢, 问题来了. 老男孩现在没有这么多学生啊. 一次性给我这么多. 我往哪里放啊. 很尴尬
啊. 最好的效果是什么样呢? 我要1套. 你给我1套. 一共10000套. 是不是最完美的.
def cloth():
for i in range(0, 10000):
yield "衣服"+str(i)
cl = cloth()
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
区别: 第一种是直接一次性全部拿出来. 会很占用内存. 第二种使用生成器. 一次就一个. 用多
少生成多少. 生成器是一个一个的指向下一个. 不会回去, __next__()到哪, 指针就指到哪儿.
下一次继续获取指针指向的值.
接下来我们来看send方法, send和__next__()一样都可以让生成器执行到下一个yield.
def eat():
print("我吃什么啊")
a = yield "馒头"
print(a)
b = yield "大饼"
print(b)
c = yield "韭菜盒子"
print(c)
print("程序结束")
yield "GAME OVER"
gen = eat() # 获取生成?
ret1 = gen.__next__()
print(ret1)
ret2 = gen.send("胡辣汤")
print(ret2)
ret3 = gen.send("狗粮")
print(ret3)
ret4 = gen.send("猫粮")
print(ret4)
send和__next__()区别:
1. send和next()都是让生成器向下走一次
2. send可以给上一个yield的位置传递值, 不能给最后一个yield发送值. 在第一次执行生
成器代码的时候不能使用send()
以上是关于day13的主要内容,如果未能解决你的问题,请参考以下文章