浅析Python迭代器、可迭代对象以及生成器
一、迭代器
1、迭代器定义
- 当类中定义了
__iter__
和__next__
两个方法。 __iter__
方法需要返回对象本身, 即:self
__next__
方法,返回下个数据,如果没有数据了,则需要抛出一个StopIteration
的异常。
鸭子类型:语言层面约定一个对象当满足一些鸭子的特点的时候,比如嘎嘎叫,会游泳,就可以把这个对象当做鸭子。
所以当一个对象满足如上迭代器限制条件的时候,就可以将其当做迭代器对象。
首先创建迭代器类型
class IT(object):
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
return self.counter
可以将迭代理解为逐步获取数据。当无法获取数据就会抛出异常。
2、获取数据
根据迭代器类型实例化可得到迭代器对象,调用魔法方法__next__
逐步获取数据。如下
obj = IT()
v1 = obj.__next__()
v2 = obj.__next__()
v3 = obj.__next__() # raise StopIteration
但是通常不希望类型内部的一些定义暴露给外部,也就是最好不要自己定义或者直接调用双下划线开头的魔法方法,关于命名规则请看一.3:Python命名下划线,可以调用next
内置函数,迭代获取数据。如下
obj = IT()
v1 = next(obj)
v2 = next(obj)
v3 = next(obj) # raise StopIteration
只不过使用next
内置函数,不仅要自己一步一步取调用,还需要自己处理因无法数据而抛出的异常。所以可以直接使用for
循环处理迭代器对象,如下
obj = IT() # obj是一个迭代器对象
for item in obj:
print(item)
for循环内部在循环时,先执行_iter_
方法, 获取一个迭代器对象,也就是自身,然后不断执行的next
取值(直到抛出异常stopIteration终止循环)
3、Python下划线命名
额外记录一下,单下划线和双下划线在Python变量名和方法名中都有各自的含义。但是更多是作为约定,或者说建议,而非强制性。
最多的四种类型:
- 前置单下划线:
_var
:前置单下划线开头的通常只是约定变量方法是内部私有,不建议外部使用,但是也未禁止。约定下划线开头的函数是模块私有,其他模块无法使用通配符导入调用。 - 前置双下划线:
__var
:前置双下划线会触发解释器的变量名称改写机制,如果是类变量会变成_类__var
的形式,避免父类子类继承时候,子类变量对父类变量的覆盖。 - 后置单下划线:
var_
:后置单下划线可以用来解决一下关键字命名冲突,比如class
与class_
。 - 前后双下划线:
__var__
:前后双下划线魔法属性魔法方法了,属于语法规定,自定义要避免,也要避免调用。 - 单下划线:
_
:用作一些无关变量,比如列表生成式中[_ for _ in range(10)]
用_
做匿名变量,或者拆包中*_, a, b = [1, 2, 3, 4, 5]
用*_
来接受1,2,3三个不需要的值。
具体Python下划线变量见:传送门
二、生成器
1、生成器简介
生成器编写方式和表现方式与迭代器不同,但是生成器可以看作是一种特殊的迭代器,因为内部也声明了__iter__
和__next__
方法
# 创建生成器函数
def fun():
yield 1
yield 2
# 创建生成器对象(内部是根据生成器类generator创建的对象,生成器内部也声明了__iter__、__next__方法)
obj = func()
v1 = next(obj)
v2 = next(obj)
v2 = next(obj) # raise异常
带yield
关键字的函数可以看做是生成器对象,所以刚开始obj = func()
的时候,并不会直接执行函数,而是调用next
的时候才会逐步执行生成数据。
2、生成器示例
示例:使用生成器自定义range
class Xrange:
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
counter = 0
while counter < self.max_num:
yield counter
counter += 1
obj = Xrange(100)
for item in obj:
print(item)
3、生成器面试题
>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 内存地址>
讲上述列表生成式中的[]改成()之后,L从列表变成了生成器。
列表生成式可以创建一个列表。但是,受到内存限制,列表容量也是受限。创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问后边的几个元素,使用列表生成式就需要创建一个巨大的列表空间保存大部分元素,太浪费。因此,没有必要创建完整的列表(节省大量内存空间)。在Python中,我们可以采用生成器:边循环,边计算的机制—>generator。生成器不会在内存中存储所有的数据,逐步生成。
三、可迭代对象
1、可迭代对象定义
定义:如果一个类中实现了__iter__
方法,并且返回的是一个迭代器对象(生成器对象,因为生成器是特殊的迭代器),可以称这个类实例化的对象为可迭代对象。
伪码示例如下:
class Foo():
def __iter__(self):
return 迭代器对象
obj = Foo()
for item in obj:
pass
以上,obj为可迭代对象。可迭代对象是可以用for来循环,先调用__iter__方法获取迭代器对象,然后调用迭代器对象的__next__()方法逐步迭代数据。
2、可迭代对象实现
迭代器与可迭代对象实例如下
# 迭代器对象类型 实例化成迭代器对象
class IT(object):
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
return self.counter
# 可迭代对象类型,实例化成可迭代对象
class Foo():
def __iter__(self):
return IT() # 返回一个迭代器对象
obj = Foo()
# 循环可迭代对象,先从obj.__iter__获取迭代器对象
for item in obj:
print(item)
range实例:range其实也是一个可迭代类型,实例化之后获取可迭代对象。通过dir
自省查看魔法方法
r = range(100) # 可迭代对象
dir(r) #[... '__iter__'...]
# 只有iter,没有next说明r是可迭代对象,不是迭代器对象
v = r.__iter__() # r调用iter之后获取v为迭代器对象
dir(v) #[... '__iter__'..., '__next__'] # 有iter和next说明v是迭代器对象
四、总结
1、判断类型
常见的一些容器类型比如列表。 属于可迭代对象,有 __iter__
无__next__
方法。
可以通过collections.abc
模块提供的Iterator,Iterable
判断对象为可迭代对象或者迭代器。
from collections.abc import Iterator, Iterable
# Iterator判断是否是迭代器对象
# Iterable判断是否是可迭代对象
m = [1, 2, 3]
n = m.__iter__()
isinstance(m, Iterator) # False
isinstance(n, Iterator) # True
isinstance(m, Iterable) # True
isinstance(n, Iterable) # False
2、三种类型小结
- 可迭代对象:含
__iter__
方法,且该方法返回一个迭代器。 - 迭代器对象:含
__iter__
方法和__next__
方法,其中__iter__
方法返回对象自身(self),__next__
方法返回迭代的数据,没有数据时需要抛出StopIteration
异常,以终止迭代。 - 生成器:在函数内使用
yield
关键字,每次调用函数类似于对迭代器执行next()
方法,生成器实际是一种特殊的迭代器。
3、迭代器与可迭代对象?
有了迭代器为什么还要可迭代对象?都是要逐步获取数据,两者重复吗?
因为可迭代对象可以通过增加类方法实现更多的功能,比如list,它是个可迭代对象,但是它的功能远远超出迭代器,还有一些比如append,clear,copy等等的方法。迭代器可以当做一个强大的类的配件。
注:以上仅为学习记录总结笔记,如有错误或者相同,轻喷,谢谢谢谢。
posted on 2021-12-15 20:12 weilanhanf 阅读(177) 评论(0) 编辑 收藏 举报