前言
大家都知道Python有功能强大三个器:装饰器、迭代器、以及生成器
但是我对这三个工具的理解过程可是花费了不少的时间,踩了不少的坑。
上篇文章谈了一下我对Python装饰器的理解
那么这篇文章再谈一谈我对迭代器以及生成器的理解。
内容仅供参考,如有错误,欢迎指正
迭代器(作用:节省空间)
- 迭代(iterate)意为着重复多次,就像循环那样
- 实现了__iter__方法的对象是可迭代的,而同时实现了__iter__和__next__的对象是迭代器
- 调用__next__方法时或者(next()),迭代器返回其下一个值
- 如果迭代器没有可供返回的值,触发StopIteration异常
- 迭代器的作用是保存一个方法 而不是结果 减少内存空间的占用
如果一个对象是迭代器 那么他一定实现了_iter__和_next__方法
首先理解for循环遍历原理(重点)
for i in [1,2,3,4,5]:
print(i)
# for循环遍历的对象一定是一个可迭代的对象
# 可以使用isinstance(对象, Iterable) 返回T/F 来判断是否为可迭代对象
# 实现了__iter__方法的对象就是一个可迭代的对象
# 以双下划线开头的方法是类的魔法方法 在创建对象的时候会自动调用
# for 循环遍历其实是执行了一下一系列操作
for temp in obj:
pass
# 1.判断obj是否是可迭代对象(即看obj对象中是否有__iter__方法)
# 2.在第一步成立的情况下,调用iter函数,即iter(obj) 执行这个方法会去调用类中实现的__iter__方法
# 然后得到了__iter__方法的返回值
# 3.__iter__方法的返回值应该是一个迭代器对象(即实现了__iter__和__next__方法的对象)
# 4.每次遍历都会执行迭代器对象中的__next__方法
面试题 range和xrange的区别
在python2解释器下,range(10)会直接返回一个0-9的列表 占用系统空间较多
而xrange(10)并不会直接返回列表,他返回的是一个xrange()迭代器对象
这个对象中保存了range的方法
即xrange返回的是一个可迭代对象,什么时候用,什么时候去,占用系统空间极少
在python3中,range()方法已经完全变成了xrange()的用法 返回可迭代对象
实例中理解
两个类实现迭代器,目的:能够使用for循环遍历出类中定义的列表中的数据
**
import time
from _collections_abc import Iterable
from _collections_abc import Iterator
class Classmate(object):
# 初始化方法定义了一个名为names的空列表
def __init__(self):
self.names = list()
# 使用add方法向names中添加数据
def add(self, name):
self.names.append(name)
# 只有实现了__iter__方法的对象,才是一个可迭代的对象
def __iter__(self):
return ClassIterator(self) # __iter__方法返回的应该是一个迭代器对象
class ClassIterator(object):
# 这个类中实现了__iter__和__next__方法 因此这个类创建出来的实例对象就是一个迭代器对象
def __init__(self, obj):
self.obj = obj
self.current = 0
def __iter__(self):
pass
def __next__(self):
if self.current < len(self.obj.names):
ret = self.obj.names[self.current]
self.current += 1
return ret
else:
raise StopIteration
classmate = Classmate()
classmate.add('老王')
classmate.add('老李')
classmate.add('老张')
# iter返回迭代器对象
# classmate_iterator = iter(classmate)
# print('判断classmate_iterator是否为迭代器', isinstance(classmate_iterator, Iterator)) # True
# print('判断classmate是否为可迭代对象', isinstance(classmate, Iterable)) # True
# next函数会是迭代器对象自动调用__next__()方法
# print(next(classmate_iterator))
# print(next(classmate_iterator))
for name in classmate:
print(name)
time.sleep(1)
# 输出---------------------------------------------
老王
老李
老张
进阶:一个类实现迭代器,目的:能够使用for循环遍历出类中定义的列表中的数据
import time
from _collections_abc import Iterable
from _collections_abc import Iterator
class Classmate(object):
def __init__(self):
self.names = list()
self.current = 0
def add(self, name):
self.names.append(name)
# 只有实现了__iter__方法的对象,才是一个可迭代的对象
def __iter__(self): # __iter__返回的一定是一个迭代器对象 只有实现了__iter__和__next__方法的对象才是迭代器对象
return self # 因此这里直接返回self,self就是该类实例对象的代表,但是此类中只实现了__iter__方法
def __next__(self): # 因此在实现__next__方法即可 这样就实现了
if self.current < len(self.names):
ret = self.names[self.current]
self.current += 1
return ret
else:
raise StopIteration
classmate = Classmate()
classmate.add('老王')
classmate.add('老李')
classmate.add('老张')
# iter返回迭代器对象
# classmate_iterator = iter(classmate)
# print('判断classmate_iterator是否为迭代器', isinstance(classmate_iterator, Iterator))
# print('判断classmate是否为可迭代对象', isinstance(classmate, Iterable))
# next函数会是迭代器对象自动调用__next__()方法
# print(next(classmate_iterator))
# print(next(classmate_iterator))
for name in classmate:
print(name)
time.sleep(1)
# 输出-------------------------------------
老王
老李
老张
生成器(作用:同样是节省空间)
- 生成器是一种使用普通函数语法定义的迭代器
- 包含yield语句的函数都被称为生成器
- 不使用return返回一个值,而是可以生成多个值,每次一个
yield和return的区别(个人理解)相当于移交控制权
# return和yield的区别
def test():
# return a
# yield a
print('hahaha')
# 执行return后 会真真正正的返回a的值,但是下面的代码不会再运行
# 使用yield后 相当于该函数变成了一个生成器模板 就像类是对象的模板一样
# 如果下面有生成器对象 当生成器被唤醒后,即调用了next或者send方法之后 yield会卡住 并且将a的值传递给next后的结果
# 当生成器再次被唤醒时, 会接着执行yield下面的代码
实例中理解
使用生成器实现斐波那契数列
# 使用生成器完成斐波那契数列
# yield 如何理解
# yield 相当于移交控制权 执行到yield后会卡住,不在继续执行,然后yield的返回值拿到for循环调用处使用, 可以理解为暂时中止,等到for循环执行后
# 接着执行yield下面的语句
def create_num(all_num):
a, b = 0, 1
current = 0
while current < all_num:
yield a # 如果一个函数中有yield语句,那么这个就不是函数了 而是一个生成器模板 相当于类 和类是一个效果
a, b = b, a+b
current += 1
return 'ok' # 只有在程序抛出异常的时候才会有返回值 因为yield的存在,会反复执行并且递交数据
# 如果在调用create_num的时候,发现函数中有yield,那么此时不是调用函数,而是创建一个生成器对象
obj = create_num(10) # obj就是生成器对象 create_num相当于类
# print(obj)
res = next(obj)
print(res)
res = next(obj)
print(res)
print('-' * 40)
for i in obj:
print(i)
# 输出---------------------------------------
0
1
----------------------------------------
1
2
3
5
8
13
21
34
生成器两种唤醒方式 next()和send()
# 使用生成器完成斐波那契数列
def create_num(all_num):
a, b = 0, 1
current = 0
while current < all_num:
ret = yield a # 如果一个函数中有yield语句,那么这个就不是函数了 而是一个生成器模板
print('>>>ret>>>>', ret)
a, b = b, a+b
current += 1
# 生成器对象obj
obj = create_num(10)
# 唤醒方式1.调用next方法
res = next(obj)
print(res)
# 唤醒方式2.调用send()方法 区别在于是否可以传入参数
# 传入参数是谁就相当于yield返回值是谁
ret = obj.send('hahha')
print(ret)
# 输出------------------------------
0
>>>ret>>>> hahha
1
使用迭代器和生成器模拟range函数迭代效果
def use_range():
'''python内置range函数'''
for i in range(5,10):
print(i)
class IterRange(object):
'''使用迭代器来模拟range函数'''
def __init__(self, start, end):
self.start = start - 1
self.end = end
def __next__(self):
self.start += 1
if self.start >= self.end:
raise StopIteration
return self.start
def __iter__(self):
return self
class GenRange(object):
'''使用生成器模拟range函数'''
def __init__(self, start, end):
self.start = start - 1
self.end = end
def gen_num(self):
while True:
if self.start >= self.end - 1:
break
self.start += 1
yield self.start
def get_num(start, end):
'''使用函数模拟range函数功能'''
start -= 1
while True:
if start >= end - 1:
break
start += 1
# 使用用yield的函数就是一个生成器对象
yield start
if __name__ == '__main__':
use_range()
print('迭代器模拟range'+'-' * 50)
iter = IterRange(5, 10)
l = list(iter)
print(l)
print('生成器模拟range'+'-' * 50)
gen = GenRange(5, 10).gen_num()
print(gen)
print(list(gen))
print('函数模拟range'+'-' * 50)
res = get_num(5, 10)
print(res)
print(list(res))
# 输出结果---------------------------------------
5
6
7
8
9
迭代器模拟range--------------------------------------------------
[5, 6, 7, 8, 9]
生成器模拟range--------------------------------------------------
<generator object GenRange.gen_num at 0x000002451F3DE7C8>
[5, 6, 7, 8, 9]
函数模拟range--------------------------------------------------
<generator object get_num at 0x000002451F3DE848>
[5, 6, 7, 8, 9]
以上是我对Python迭代器和生成器的理解,希望能够帮助到大家。
如果有错误,请大家指出谢谢。