目录
一、可迭代对象
可迭代对象,就是 可以使用for 循环遍历的对象。
比如 list 列表
tuple 元组
set 集合
str 字符串
dic 字典
如果想要使用for来遍历,这个对象必需具有 __iter__ 方法
1.1 判断是否为可迭代对象
方法一:
from typing import Iterable
isinstance(obj,Iterable)
方法二:
是否有__iter__ 方法
print(hasattr(list,"__iter__"))
print(hasattr(str,"__iter__"))
print(hasattr(tuple,"__iter__"))
print(hasattr(dict,"__iter__"))
print(hasattr(set,"__iter__"))
True
True
True
True
True
二、迭代器
1、迭代器首先是一个可迭代对象,拥有__iter__ 方法,返回self
2、还必须要有 __next__ 方法。__next__ 方法 用来输出下一个元素。如果没有下一个值,则抛出StopIterator 异常
3、迭代器,使用for操作时,得到的每一个元素,会自动的调用__next__方法,从而得到下一个元素,依次循环,这样就能遍历所有的元素了。for语句迭代会忽略异常。
4、迭代器迭代的时候,使用for,不要使用while
5、迭代器只能迭代一次。(因为对象里用于迭代计算的下标一直是增加的)
6、迭代器比list占用更小的内存。
2.1 判断对象是否是一个迭代器
迭代器同时拥有 __iter__ 方法 与 __next__ 方法。
方法一:
isinstance(obj,Iterator)
注意,这里是Iterator 不是Iterable
列表list,是一个可迭代对象,但不是一个迭代器。
from typing import Iterable, Iterator
a = [1,2,3]
print(isinstance(a,Iterable))
# True
print(isinstance(a,Iterator))
# False
将 可迭代对象,转化为一个迭代器,使用iter方法.
from typing import Iterable, Iterator
a = [1,2,3]
print(isinstance(a,Iterable))
print(isinstance(a,Iterator))
b = iter(a)
print(isinstance(b,Iterator))
# True
2.2 手写一个迭代器
# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2023 - 02 -18
# @File: my_iterator.py
class MyIterator(object):
def __init__(self, stop: int, start=0):
self.stop = stop
self.value = start
def __next__(self):
"""返回下一个元素,没有则抛出StopIteration 异常"""
if self.value < self.stop:
self.value += 1
else:
raise StopIteration
return self.value
def __iter__(self):
return self
if __name__ == '__main__':
myIterator = MyIterator(5)
print(myIterator.__next__())
print(myIterator.__next__())
print(myIterator.__next__())
print(myIterator.__next__())
print(myIterator.__next__())
print(myIterator.__next__())
输出结果:
1
2
3
4
5
Traceback (most recent call last):
File "/Users/zhaohui/PycharmProjects/MyTest/cekai/my_iterator.py", line 27, in <module>
print(myIterator.__next__())
File "/Users/zhaohui/PycharmProjects/MyTest/cekai/my_iterator.py", line 16, in __next__
raise StopIteration
StopIteration
使用for遍历
myIterator = MyIterator(5)
for i in myIterator:
print(i)
打印结果:
1
2
3
4
5
2.3 迭代器应用场景
迭代器,占用内存,远远小于list。
需要节省内存的场景。
三、生成器
3.1 生成器介绍
生成器:为了快速、方便的创建一个迭代器。
生成器,就是一个使用了yield 关键字的迭代器。
3.2 使用yield 关键字 生成器,来实现迭代器
举例:
list = [ 1,2,3,4,5]
每个元素平方,再生成一个新的list
new_list = [1,4,9,16,25]
方法一:
使用list实现
list = [1, 2, 3]
list1 = []
for i in list:
list1.append(i * i)
print(list1)
方法二:
手动创建一个迭代器
class Square(object):
def __init__(self, start, stop):
self.value = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.value > self.stop:
raise StopIteration
queare_num = self.value * self.value
self.value += 1
return queare_num
if __name__ == '__main__':
square = Square(1, 5)
for i,s in enumerate(square):
print(i,s)
打印结果:
0 1
1 4
2 9
3 16
4 25
方法三 使用yield 关键字,实现一个生成器
def square_yield(start:int,stop:int):
for i in range(start,stop+1):
yield i*i
if __name__ == '__main__':
square_yield(1,4)
yield 就等价于迭代器中的 __next__ 方法
yield 关键字,与 return 方法比较
def square_yield(start:int,stop:int):
for i in range(start,stop+1):
yield i*i
def squqre_return(start,stop):
for i in range(start,stop+1):
return i*i
if __name__ == '__main__':
square_yield(1,4)
print(squqre_return(1,4))
方法中,return 方法,就表示这个函数结束了。
yield 返回的是一个 生成器类。
3.3 生成器(yield关键字)的运行逻辑
1、yield 关键字,与return 一样,会返回一个结果。 但是return返回的是 运行结果
但是yield返回的,可不是运行结果,返回的是一个迭代器的对象。只有这个对象调用__next__
的时候,才会执行业务逻辑
2、 yield 会记录每次的运行位置。
3、只有生成器的对象在调用__next__方法时,才会执行业务代码。业务代码执行完,函数就会变成非runnning状态(挂起状态)。直到下一次__next__方法被调用
举几个例子,方便我们理解一下
举例一:
------没有使用yield关键字
def f1():
print("开始执行")
return "f1被执行了"
if __name__ == '__main__':
fun = f1()
当调用f1函数时,f1里面的业务代码被执行了。
------使用yield关键字
def f():
print("yield 1------------")
a = 1
yield a
print("yield 2=================")
a = 2
yield a
print("yield 3=================")
a = 3
yield a
if __name__ == '__main__':
generator = f()
运行结果为空,因为generator = f(),得到的是一个生成器的对象,没有调用业务代码。
当我们第一次调用__next__时
generator = f()
print(generator.__next__())
输出结果:
yield 1------------
1
当第二次调用__next__时
输出结果:
yield 2=================
2
当第三次调用__next__时
输出结果:
yield 3=================
3
当生成器的对象,调用__next__方法时,yield 会记录每一次 运行的位置,当 下一次调用__next__方法时,业务代码会接着上一次的位置继续运行。
如下图:
整体代码:
# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2023 - 02 -18
# @File: diedai5.py
def f():
print("yield 1------------")
a = 1
yield a
print("yield 2=================")
a = 2
yield a
print("yield 3=================")
a = 3
yield a
if __name__ == '__main__':
generator = f()
print(generator.__next__())
print(generator.__next__())
print(generator.__next__())
举例二:
解释一下上面求平方的函数,的生成器
def square_yield(start:int,stop:int):
for i in range(start,stop+1):
yield i*i
每次__next__ 都会记录 业务代码运行位置。
--------当第一次调用__next__ 时:
i = 1 ,返回 1x1 的结果。内存中记录 i = 1
--------第二次调用__next__ 时:
上一次的记录是i=1,开启for下一轮的循环,则i=2.返回 2x2 的结果。内存中记录 i = 2