首页 > 技术文章 > 5.迭代器和生成器

journeyer-xsh 2020-07-05 16:47 原文

一、迭代器

1.1可迭代对象

可迭代对象:内部含有'__iter__' 方法的对象。

# 获取一个对象的所有方法:dir()
s1 = 'asdsad'
print(dir(s1))
print('__iter__' in dir(s1))

优点:存储数据能直接显示,比较直观,

缺点:占内存,不能直接取值(除去索引,key以外)

通过for循环取值的时候,先将可迭代对象转换成迭代器,然后进行取值。

1.2迭代器

迭代器:内部含有'Iter'方法并且含有'next'方法的对象就是迭代器。

str list tuple dict set range 文件句柄 中只有文件句柄是迭代器,剩下的那些数据类型都是可迭代对象。

# 可通过,下列2个print的结果都返回True时才是迭代器
l1 = [1,2,3,4]
print('__iter__' in l1 )
print('__next__' in l1)

1.3可迭代对象转化成迭代器

l1 = [1, 2, 3, 4, 5, 6]
obj = l1.__iter__() 
# 或者 obj=iter(l1)	print(obj) 

1.4迭代器取值

可迭代对象是不可以一直迭代取值的(除去用索引,切片以及Key),转化成迭代器就可以利用__next__()进行取值

l1 = [1, 2, 3,]
obj = l1.__iter__()  # 或者 iter(l1)
# print(obj)  # 返回一个地址
ret = obj.__next__()
print(ret)
ret = obj.__next__()
print(ret)
ret = obj.__next__()
print(ret)
ret = obj.__next__()  # StopIteration

迭代器利用next取值,一个next对应一个值,如果迭代器里的值取完了,还要next就会报StopIteration错误。

1.5迭代器的小例子:while模拟for循环

l1 = [1, 2, 3, 4, 5]
# 1 将可迭代对象转化成迭代器
obj = iter(l1)
# 2,利用while循环,next进行取值
while 1:
    # 3,利用异常处理终止循环
    try:
        print(next(obj))
    except StopIteration:
        break

迭代器的优点:

  • 节省内存,迭代器在内存中只占用一条数据的空间,上一条数据next后所占内存就被释放了,然后再胡加载当前数据,这也是python把文件句柄设置为迭代器的原因。

  • 惰性机制

    next一次只取一个值

迭代器缺点:

  • 不够直观
  • 只能一直向后取值

二、生成器

2.1什么是生成器?

生成器本质就是迭代器,不同的是迭代器是python提供的,生成器要自己写。

2.2构造生成器

构造生成器的三种方式:

  • 自己写的生成器函数
  • 生成器推导式
  • python内置函数或者模块提供

生成器函数:

return和yield的区别

  • return 结束函数,并且返回值
  • yield 只要函数中有yield就是生成器函数,生成器函数中可以写多个yield
  • 生成器中可以有多个yield,yield不会结束生成器函数,一个yield对应一个next
def get_func():
    li = []
    for i in range(1, 5001):
        li.append(f'{i}')

# 节省内存
def get_func():
    for i in range(1, 5001):
        yield f'{i}'
ret = get_func()
for i in range(222):
    print(next(ret))
    
# 把列表变成迭代器
def func():
    l1 = [1, 2, 3, 4, 5]
    yield from l1   # 将l1变成了迭代器

ret1 = func()
print(next(ret1))
print(next(ret1))

yield from的简单应用:他后面可以加可迭代对象也可以是迭代器或生成器。

# 使用yield
s1 = 'an'
li = [1,2,3,4]
dic = {'name': 'cn', 'age':11}
dict1 = 

def get_yield(*args):
    for item in args:
        for i in item:
            yield i

print(list(get_yield(s1, li, dic)))
# ['a', 'n', 1, 2, 3, 4, 'name', 'age']
# 使用yield from

生成器的使用

这里的g1和g2是空的。

def func():
    print(111)
    yield 222

g = func()  # 生成器
g1 = (i for i in g)
g2 = (i for i in g1)


print(list(g))  # 回取出func中的数据,打印111,获取222
print(list(g1)) # 获取g1的数据,g1的数据来源是g,但是g已经取完了,g1也就没有数据了
print(list(g2))     # 和g1同理

生成器函数() -->返回的是一个地址

# 列表推导式习题
# 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
# 输出[(0, 1), (0, 3), (0, 5), (2, 1), (2, 3), (2, 5), (4, 1), (4, 3), (4, 5)]
# print([(x, y) for x in range(0,6,2) for y in range(1,6,2)])

三、推导式

3.1列表推导式

  • 循环模式:[ 变量(加工后的变量) for 变量 in iterable ]
  • 筛选模式:[ 变量(加工后的变量) for 变量 in iterable if 条件 ]

优点:

  • 可利用简单的代码构建比较复杂有规律的列表
  • 也可以对列表中的一些元素进行筛选
#  循环模式
l1 = [i for i in rang(1, 11)]
l2 = [i**2 for i in range(10)]

# 筛选模式
# 30以内的被3整除
# print([i for i in range(0, 31) if i%3 == 0])
# 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
l1 = ['barry', 'ab', 'an', 'qwers', 'qwepopo']
# print([i.upper() for i in l1 if len(i) >= 3 ])

3.2生成器表达式

与列表推导式的写法几乎一模一样,也有筛选模式,循环模式,多层循环构建。写法上只有一个不同:

[] 换成 ()

列表的推导式和生成器的区别

  • 写法上的区别:[] ()
  • iterable iterator 可迭代对象和可迭代对象上的区别

生成器表达式和列表推导式的区别:

  • 列表推导式比较耗内存,所有数据一次性加载到内存。而.生成器表达式遵循迭代器协议,逐个产生元素。
  • 得到的值不一样,列表推导式得到的是一个列表.生成器表达式获取的是一个生成器
  • 列表推导式一目了然,生成器表达式只是一个内存地址。

3.3其他推导式

字典推导式

lst1 = ['an','bn','cn']
lst2 = ['aa','bb','cc']
dic = {lst1[i]:lst2[i] for i in range(len(lst1))}
print(dic)		# -->{'an': 'aa', 'bn': 'bb', 'cn': 'cc'}

集合推导式

lst = [1,2,3,4,5,6,7]
s = {abs(i) for i in lst}
print(s)

集合推导式可以帮我们直接生成一个集合,集合的特点;无序,不重复 所以集合推导式自带去重功能

推荐阅读