python中的迭代器对象_可迭代对象、迭代器对象和生成器对象

可迭代对象iterable

字面意思:

可迭代:更新迭代。迭代是一个重复的过程,每次重复是基于上一次的结果而继续的,每次都有新的内容。

可迭代对象:可以进行循环更新的一个实实在在的值。

专业角度:

内部含有'__iter__'方法的对象。

str.__iter__

list.__iter__

set.__iter__

dict.__iter__

tuple.__iter__

优点:

1、存储的数据能直接显示,比较直观。

2、拥有的方法比较多,操作方便。

缺点:

1、占内存,有多少值就会占用多少内存。

2、只能通过索引,key取值。

for循环能进行取值是因为在底层做了转换,将可迭代对象转换成迭代器,然后取值。

迭代器对象iterator

字面角度:

迭代器指的是迭代取值的工具(数据结构)。

专业角度:

内置有__next__方法和__iter__方法的对象。

f = open('a.txt',mode'rt',encoding='utf-8')

print('__iter__' in dir(f),'__next__' in dir(f))

f.close()

True True

文件对象就是迭代器,这是为了避免当一个文件过大时,占用过多内存,所以Python做的优化。

优点:

1、提供一种不依赖索引的通用迭代取值方案。

2、惰性计算,节省内存。每次在内存中仅存在一个值。

缺点:

1、速度慢,以时间换空间。

2、取值麻烦。

先将可迭代对象调用__iter__方法或iter()函数转换成迭代器,然后调用__next__方法或next()函数取值。

li = [11,22,33]

li_obj = li.__iter__()

print(li_obj.__next__())

print(li_obj.__next__())

print(li_obj.__next__())

print(li_obj.__next__())

每调用一次__next__就会取一次值,当值都取完后,再调用__next__会抛StopIteration异常。

StopIteration

11

22

33

3、没有__len__方法,无法预测值的长度,只能取到抛出异常才知道已经取完。

s1 = {11,22,33}

obj = iter(s1)

print('__len__' in dir(obj))

False

4、一次性取值。

li = [11,22,33]

li_obj = iter(li)

for i in li_obj:

print(i)

print('====分隔符====')

for i in li_obj:

print(i)

在第一个for循环取完值后,第二次for循环已经取不出来值了。所以结果为:

11

22

33

====分隔符====

除非重新调用iter()转换成迭代器,就可以重新取值。

li_obj2 = iter(li)

for i in li_obj:

print(i)

11

22

33

====分隔符====

11

22

33

使用while循环模拟for循环对迭代器取值

1、调用可迭代对象.__iter__方法,得到一个迭代器对象。

2、迭代器调用next(),获取一个值。

3、循环取值,直到抛出StopIteration异常,捕捉异常然后结束循环。

s1 = {11,22,33}

obj = iter(s1)

while 1:

try:

print(next(obj))

except StopIteration: # 当捕捉到StopIteration异常时就break结束循环。

break

将可迭代对象转换为迭代器就可以实现不依赖索引或key也能取值的目的。

可迭代对象转换为迭代器对象

可迭代对象调用__iter__方法或iter()函数会转换成迭代器。每次转换得到的都是一个新的迭代器。

li = [11,22,33]

li_obj = iter(li)

li_obj2 = iter(li)

print(li_obj is li_obj2) # 两次都得到一个新的迭代器。

False

两个迭代器都可以取值。

for i in li_obj:

print(i)

for i in li_obj2:

print(i)

11

22

33

11

22

33

如果是迭代器对象调用__iter__方法或iter()函数,所得到的结果为迭代器对象本身。这么设计是为了方便for循环的工作机制,使迭代器对象无论调用__iter__方法多少次,得到的结果都是迭代器对象自身。

li = [11,22,33]

li_obj = iter(li)

li_obj2 = li_obj.__iter__()

li_obj3 = li_obj2.__iter__().__iter__()

print(li_obj is li_obj2 is li_obj3)

# 无论调用多少次,结果还是迭代器自身。

True

for 循环工作原理

1、调用可迭代对象.__iter__方法,得到一个迭代器对象。

2、调用next(迭代器),将返回值赋值给in前面的变量。

3、循环取值,直到抛出StopIteration异常,for就会捕捉异常然后结束循环。

s1 = {11,22,33}

for i in s1:

print(i)

33

11

22

由于迭代器对象无论调用__iter__方法多少次,得到的结果都是迭代器对象自身,所以for循环无论迭代的是可迭代对象还是一个迭代器对象,都可以直接调用该对象的__iter__方法,简化了for循环的设计思路。

内置函数dir()

获取一个对象的所有内置方法,以列表的形式返回。

l = [1,2,3]

print(dir(l))

['__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']

判断一个对象是否是可迭代对象。

s = 'abc'

print('__iter__' in dir(s))

True

生成器generator

生成器的本质就是迭代器。

区别:

生成器是我们自己用Python代码构建的数据结构。迭代器是Python提供的,或者调用iter()转换得来的。

def f():

yield 10

print('__iter__' in dir(f()) and '__next__' in dir(f()))

True

获取生成器的两种方式:

1、yield构建生成器函数。

2、生成器表达式。

1、yield构建生成器函数。

函数体内但凡出现yield关键字,调用函数将不会触发函数体代码的运行,而是会返回一个生成器对象。

def func():

print(111)

yield 1

print(222)

yield 3

ret = func()

print(ret)

print(next(ret))

111

1

特点:

只要函数中有yield那么就是生成器函数。

生成器函数加括号()并不会直接执行函数体代码,而是返回一个生成器对象。

def func():

print(111)

yield 'aaa'

obj = func()

print(obj)

# 并不会打印111

每调用一次生成器函数都会获得一个新的生成器。

def func():

print(111)

yield 'aaa'

obj = func()

obj2 = func() # 会得到一个新的生成器.

print(obj is obj2)

False

函数中可以存在多个yield,并且yield不会终止函数。

通过next取出yield返回的值,并会保留上次取值的位置。一个next对应一个yield,多了会报错。

也就是说,yield可以暂停函数,然后我们可以拿到返回值进行处理后,用next方法再次触发函数代码的运行,协程方面有应用。

return与yield的区别:

相同点:在返回值反面用法一样。

不同点:yield可以返回多次值,而return只能返回一次。

用生成器函数手撸个range功能。

def my_range(start,stop,step=1):

while start < stop:

yield start

start += step

for i in my_range(0,10):

print(i)

0

2

4

6

8

用生成器函数手撸个无限取值的功能。

def get_num():

num = 0

while 1:

yield num

num += 1

obj = get_num()

print(next(obj))

print(next(obj))

print(next(obj))

结果为:

0

1

2

每调用一次生成器函数都会获得一个新的生成器,如果用下述写法,每次得到的是个新生成器,永远是取0:

def get_num():

num = 0

while 1:

yield num

num += 1

print(next(get_num()))

print(next(get_num()))

0

0

2、生成器表达式。

首先讲列表推导式:能用一行代码构建一个比较复杂有规律的列表。

格式:

1、循环模式:

l = [表达式 for 变量 in iterable]

# 多层嵌套

l = [表达式 for 变量 in iterable for 变量 in iterable ...]

2、筛选模式:

l = [表达式 for 变量 in iterable if 条件 for 变量 in iterable]

超过三层循环才能构建成功的,不建议使用列表推导式。查找错误(debug模式)不行。

# 常规创建

li = []

for i in range(1,10):

if i > 3:

li.append(i)

# [4, 5, 6, 7, 8, 9]

# 列表推导式:

li = [i for i in range(1,10) if i > 3]

print(li)

# [4, 5, 6, 7, 8, 9]

生成器表达式与列表推导式的写法几乎一模一样。将列表推导式的方括号改为圆括号。也有筛选模式,循环模式,多层嵌套。

实例:

l = (i for i in range(10))

print(next(l))

print(next(l))

print(next(l))

0

1

2

字典推导式:

l1 = ['jay','jj','meet']

l2 = ['周杰伦','林俊杰','元宝']

dic = {l1[i]:l2[i] for i in range(len(l1))}

print(dic)

# {'jay': '周杰伦', 'jj': '林俊杰', 'meet': '元宝'}

items = [('a',1),('b',2),('c',3)]

dic = {key:value for key,value in items if value > 1 }

print(dic)

# {'b': 2, 'c': 3}

集合推导式:

s1 = {i for i in range(1,4)}

print(s1)

{1, 2, 3}

并没有元组推导式,因为有列表推导式就可以使用tuple()将列表转换成元组。

li = tuple([i for i in range(1,10) if i > 3])

print(li)

(4, 5, 6, 7, 8, 9)

表达式应用:

计算一个文件内有多少个字符。

with open('user.txt',mode='r',encoding='utf-8') as f:

# 常规方法,读取每一行计算字符数,然后再累加

d = 0

for line in f:

d += len(line)

print(d)

# 可使用列表推导式简化,但文件行数若过多,会造成列表元素过多,占用大量内存空间

d = sum([len(line) for line in f])

print(d)

# 使用生成器表达式,每次只会占用一块内存空间

d = sum((len(line) for line in f))

print(d)

# 函数的括号和生成器表达式的括号可以合并。

d = sum(len(line) for line in f)

print(d)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值