可迭代对象
什么是迭代?
迭代言外之意就是更新换代,每次的更新都是要依赖于上一次的结果
# 什么又是可迭代对象呢?
就是内置有__iter__()方法的都可以称之为是可迭代对象
"内置":可以直接点出来的就称之为是内置,既然可以点出来就可以直接拿来使用
__name__:属性
__iter__():方法
"""
__iter__()
双下划线开头的方法我们是第一次遇到,他是一种特殊的方法
像双下划线开头的方法在面相对象里面最常见
用法跟普通方法都是一样的
"""
# 我们所学的八种基本数据类型都有哪些是可迭代对象呢?
"""
是可迭代对象的有:
字符串、列表、元组、字典、集合等
整型、浮点型、布尔值都不是
ff = open('a.txt', 'w', encoding='utf8')
文件对象也是可迭代对象,因为内置的有__iter__()方法
"""
可迭代对象调用__iter__()方法就变成了"迭代器对象"
print(d.__iter__())
print(iter(d))
def iter(obj):
return obj.__iter__()
# print(len(d))
# print(d.__len__())
def len(obj):
return obj.__len__()
"""
一般情况下,双下滑先开头的方法都有一个与之对于的简化方法 名字()
"""
迭代器对象
迭代器对象:迭代器
什么是迭代器对象?
既内置了__iter__()方法,又内置了__next__方法就是迭代器对象
"""文件对象本身就具备__iter__()和__next__()方法"""
即是可迭代对象又是迭代器对象
# 到底都有哪些数据类型是迭代器对象
str、list、dict、tuple、set、文件等
# 迭代器对象有什么用呢
"""
迭代器其实是一种不依赖于索引取值的方式!
"""
可迭代对象多次调用__iter__()方法之后还是迭代器对象,所以你只需要调用一次即可
l = [1, 2, 3, 4]
# print(l[0])
# print(l[1])
# print(l[2])
res1 = l.__iter__()
# print(res1.__next__())
# print(res1.__next__())
# print(res1.__next__())
print(next(res1))
print(next(res1))
print(next(res1))
d = {'username': 'ly', 'age': 18}
# res = d.__iter__()
# print(res.__next__())
# print(res.__next__())
# print(d.__iter__())
# print(d.__iter__().__iter__().__iter__().__iter__())
"""易错题"""
ll = [1, 2, 3, 4]
# res = ll.__iter__()
# print(res.__next__())
# print(res.__next__())
# print(res.__next__())
# print(res.__next__())
# print(res.__next__()) # StopIteration 当数据被取值完的时候,如果在次next会直接报错
print(ll.__iter__().__next__()) # 1
print(ll.__iter__().__next__()) # 1
print(ll.__iter__().__next__()) # 1
print(ll.__iter__().__next__()) # 1
res = ll.__iter__()
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
异常捕获
try
被监测的代码:一般是可能会发生的错误
except 错误类型1 as e
print(e)
except 错误类型2 as e
print(e)
else:
print('看一下else什么时候走的?')
finally:
print('看一下finally什么时候走?')
"""
没有异常的时候else会走,有异常的时候else不会走
finally是不管有没有异常都会走
"""
# 这里面你只需要记住try和else不能单独使用,
for循环的原理
l = [1,2,3,4,5,6,7,8]
# 循环打印出列表中的每一个元素,但是不能使用for循环,必须要使用__next__()取值
l = [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
# 循环打印出列表中的每一个元素,但是不能使用for循环,必须要使用__next__()取值
res = l.__iter__()
while True:
try:
print(res.__next__())
except Exception:
break
相当于:
for i in l:
print(i)
for循环的原理:
1. 先把关键字in后面的可迭代对象先调用__iter__()
2. while循环,next()取值
3. for的内部当出现异常的时候,做了捕捉处理:StopIteration的错误,break
迭代取值和索引取值的对比
迭代取值
1. 不依赖于索引取值
2. 只能从做往右依次取值,不能重复取值
索引取值
1. 必须依赖于索引取值
2. 可以重复取值
l[0]
3. 必须是容器类型
生成器(迭代器的一种)
"""
生成器的背景:
l = [1, 2, 3]
我们现在定义的列表,内部元素都比较少,占用的内存也是比较少的,我们几乎可以忽略,但是,如果我现在让你定义一个列表,里面存放100w个元素,可是呢,我们只用到了前面或者后面几个元素,其他元素很少用,如果我们把这100w个元素都写在列表里,并且每次使用的使用都打印了,会出现什么问题?
所以,这个时候就会占用大量的内存空间,这些空间就被浪费了,针对这个问题,该如何优化一下呢
优化的方法就是利用生成器:
如果你不用这些数据,那么在你打印的时候,我给给你一个对象,不给你具体的数据,你什么时候使用这些数据,我在给你
"""
# 生成器解决的问题是什么/
就是节省内存空间
range(10) 在python3中是一个生成器,节省了内存空间
# 到底如何使用
def index():
print('from index')
yield 123,234,345
"""函数里面只要出现了yield关键字,那么,该函数就有普通的函数变成了生成器,就不在执行该函数了"""
res=index()
# print(res) # <generator object index at 0x000001F5008287B0>
# 要想使用生成器只需要使用next取值即可
# 我们使用next取值,每next一次,代码走到第一个yield处停止,
print(res.__next__()) # None (123, 234, 345)
# print(res.__next__()) # None 再次执行next,会从上一次yield的位置处往下继续走,走到遇到第二个yield停止
"""如果你打印next的结果,就会返回yield关键字后面的数据"""
# 当yield关键字后面的数据用逗号隔开,有多个的时候,会以元组的形式返回
自定义rangge功能
range(10)
range(1, 10)
range(1, 10, 2)
# range函数不能用了,我让你写一个跟range函数一样的功能? 使用生成器来做:yield
# range()
# None:假 not None
def my_range(start, stop=None, step=1):
# 判断传了一个参数还是两个参数
if not stop:
stop = start # stop=10
start = 0 # start =0
while start < stop:
yield start
start+=step
res=my_range(1, 10)
# print(res.__next__())
# print(res.__next__())
# for i in my_range(10, 100, 2): # __next__
# print(i)
for i in my_range(10): # __next__
print(i)
yield关键的传参问题
def eat(name):
print('%s正在干饭' % name)
while True:
food = yield
print('%s正在吃%s' % (name, food))
# 函数里面只要有yield关键字,就不会执行函数,变成了生成器
res=eat('kevin')
# res.__next__()
res.__next__()
"""
1. 把参数传给了yield
2. 执行了__next__取值
"""
res.send('馒头')
res.send('馒头1')
res.send('馒头2')
return和yield的对比
return
1. 函数遇到return直接终止运行
2. return也可以返回多个值
yield
1. 函数遇到yield代码不会立即终止运行,而是"停住"
2. yield也可以返回多个值,以元组的形式返回
3. yield可以把函数变成生成器,而且还支持传参
生成器表达式
列表生成式:
res = [i for i in range(10)]
生成器表达式:
res1 = (i for i in range(10))
print(res1) # <generator object <genexpr> at 0x0000018083EA3A50>
print(res1.__next__())
print(res1.__next__())
print(res1.__next__())
print(res1.__next__())
print(res1.__next__())
"""生成器表达式如果你不去__next__,是不会给你造出来数据的"""
迭代器和生成器什么关系:
'''生成器他是特殊的一种迭代器'''
"""
迭代器、生成器我们都可以把它们看成是"工厂"
你什么时候要数据我们就设么时候给你生产
上述这样做的原因:
节省内存空间
"""
如果我们要读取一个大文件的字节数,应该基于生成器表达式的方式完成
with open('db.txt','rb') as f:
nums=(len(line) for line in f)
total_size=sum(nums) # 依次执行next(nums),然后累加到一起得到结果=
面试题
# 求和
def add(n, i):
return n + i
# 调用之前是函数 调用之后是生成器
def test():
for i in range(4):
yield i
g = test() # 初始化生成器对象
for n in [1, 10]:
g = (add(n, i) for i in g)
res = list(g)
print(res)
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]
#D. res=[21,22,23,24]
选C