迭代器与生成器

Posted williamkong94

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迭代器与生成器相关的知识,希望对你有一定的参考价值。

迭代器

迭代器:迭代的工具。迭代可以看成重复,并且每一次的重复都是基于上次的结果来的,不是单纯的重复。

可迭代对象

python中一切皆对象,如:

x = 1
name = 'william'
lis = [1, 2, 3, 4]
tup = (1, 2, 3, 4)
dic = 'name': 'william'
s = 'a', 'b'


def func():
    pass


f = open('602.txt', 'w', encoding='utf8')

对于这一切对象,只要有__iter__方法的对象,都是可迭代对象。

# x = 1.__iter__   # SyntaxError: invalid syntax

# 以下都是可迭代对象

name = 'william'.__iter__
lis = [1, 2, 3, 4].__iter__
tup = (1, 2, 3, 4).__iter__
dic = 'name': 'william'.__iter__
s = 'a', 'b'.__iter__
f = open('602.txt', 'w', encoding='utf8')
f.__iter__
f.close()

总结

可迭代的对象:Python内置的str,list,tuple,dict,set,file都是可迭代对象。

特点:内置有__iter__方法的都叫可迭代的对象。

迭代器对象

只有字符串和列表依赖索引取值,其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。

首先我们要知道迭代器对象的概念:可迭代对象执行__iter__方法得到的返回值。并且迭代器对象会有一个__next__方法。

# 不依赖索引的数据类型迭代取值

dic = 'a': 1, 'b': 2, 'c': 3
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
# print(iter_dic.__next__())  # StopIteration:
a
b
c
# 依赖索引的数据类型迭代取值

lis = [1, 2, 3]
iter_lis = lis.__iter__()
print(iter_lis.__next__())
print(iter_lis.__next__())
print(iter_lis.__next__())
# print(iter_lis.__next__())    # StopIteration:
1
2
3

上述的方法是非常繁琐的,我们可以使用while循环精简下。其中使用的try except为异常处理模块。

s = 'hello'
iter_s = s.__iter__()

while True:
    try:
        print(iter_s.__next__())
    except StopIteration:
        break
h
e
l
l
o

总结

迭代器对象:执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象。

特点:

  1. 内置有__next__方法,执行该方法会拿到迭代器对象中的一个值。
  2. 内置有__iter__方法,执行该方法会拿到迭代器本身。
  3. 文件本身就是迭代器对象

缺点:

  1. 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了。
  2. 无法使用len()方法获取长度。

for循环原理

for循环称为迭代器循环,in后必须是可迭代的对象。

lis = [1, 2, 3]
for i in lis:
    print(i)
1
2
3

因为迭代器使用__iter__后还是迭代器本身,因此for循环不用考虑in后的对象是可迭代对象还是迭代器对象。

由于对可迭代对象使用__iter__方法后变成一个迭代器对象,这个迭代器对象只是占用一小块内存空间,只有使用__next__后才会吐出一个一个值。如lis = [1,2,3,4,5]相当于一个一个鸡蛋,而lis = [1,2,3,4,5].__iter__相当于一只老母鸡,如果你需要蛋,只需要__next__即可。

# python2中
print(range(10))  # [0,1,2,3,4,5,6,7,8,9]
# python中
print(range(10))  # range(0,10)

三元表达式与列表推导式

三元表达式

‘条件成立时的返回值‘if‘条件‘else‘条件不成立时的返回值‘。

x = 10
y = 20

print(f'x if x > y else y : x if x > y  else y')
x if x > y else y : 20

列表推导式

[expression for item1 in iterable1 if condition1
 for item2 in iterable2 if condition2
 ...
 for itemN in iterableN if conditionN
 ]
类似于
res = []
for item1 in iterable1:
    if condition1:
        for item2 in iterable2:
            if condition2:
                ...
                for itemN in iterableN:
                    if conditionN:
                        res.append(expression)
print(f'[i for i in range(8)] :[i for i in range(8)] ')
[i for i in range(8)] :[0, 1, 2, 3, 4, 5, 6, 7]
print(f'[i**2 for i in range(8)] : [i**2 for i in range(8)]')
[i**2 for i in range(8)] : [0, 1, 4, 9, 16, 25, 36, 49]
name_list = ['william', 'tom', 'jerry']

print(
    f"[name if name == 'william' else name +'funny' for name in name_list]:[name if name == 'william' else name + 'funny' for name in name_list] ")
[name if name == 'william' else name +'funny' for name in name_list]:['william', 'tomfunny', 'jerryfunny']

字典生成式

字典生成式

print(i: i**2 for i in range(8))
0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49

zip()方法

keys = ['name', 'age', 'gender']
values = ['william', 18, 'male']

res = zip(keys, values)
print(f'zip(keys,values):zip(keys,values)')

info_dict = k: v for k, v in res
print(f'info_dict :info_dict')
zip(keys,values):<zip object at 0x00000256E9B28548>
info_dict :'name': 'william', 'age': 18, 'gender': 'male'

通过解压缩函数生成一个字典。

info_dict = 'name': 'william', 'age': 18, 'gender': 'male'
print(f'info_dict.keys():info_dict.keys()')
print(f'info_dict.values():info_dict.values()')

res = zip(info_dict.keys(), info_dict.values())
print(f'zip(keys,values):zip(info_dict.keys(),info_dict.values())')

info_dict = k: v for k, v in res
print(f'info_dict:info_dict')
info_dict.keys():dict_keys(['name', 'age', 'gender'])
info_dict.values():dict_values(['william', 18, 'male'])
zip(keys,values):<zip object at 0x00000256E9B28988>
info_dict:'name': 'william', 'age': 18, 'gender': 'male'

生成器

yield关键字

yield意思为生产,在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是会返回一个值。

def func():
    print(1)
    yield
    print(2)
    yield


g = func()
print(g)
<generator object func at 0x00000256E9B0CFC0>

生成器的本质就是迭代器,同时也并不仅仅是迭代器,不过迭代器之外的用途实在是不多,所以我们可以说:生成器提供了非常方便的自定义迭代器的途径。

def func():
    print('from func1')
    yield 'a'
    print('from func2')
    yield 'b'


g = func()
print(f'g.__iter__ == g :g.__iter__() == g')

res1 = g.__next__()
print(f'res1:res1')

res2 = next(g)
print(f'res2:res2')

# next(g)  # StopIteration
g.__iter__ == g :True
from func1
res1:a
from func2
res2:b
def func():
    print('from func1')
    yield 'a'
    print('from func2')
    yield 'b'


g = func()
for i in g:
    print(i)

print(f'list(func():list(func()))')
from func1
a
from func2
b
from func1
from func2
list(func():['a', 'b'])

yield + return??

既然生成器也是函数,那么它可以使用return输出返回值么?

既然都选择自定义一个函数作为生成器,还return干什么,Python2中会报异常,Python中,无视这种行为。

def i_wanna_return():
    yield 'a'
    yield 'b'
    return None
    yield 'c'


for i in i_wanna_return():
    print(i)
a
b

迭代器套迭代器

如果我需要在生成器的迭代过程中接入另一个生成器的迭代怎么办?写成下面这样好傻好天真。

def sub_generation():
    yield 1
    yield 2
    for i in range(3):
        yield i


for i in sub_generation():
    print(i)
1
2
0
1
2
def sub_generation():
    yield 1
    yield 2
    yield from range(3)


for i in sub_generation():
    print(i)
1
2
0
1
2

协同程序

协同程序(协程)一般来说是指这样的函数:

  • 彼此间有不同的局部变量、指令指针,但仍共享全局变量;
  • 可以方便的挂起、恢复,并且有多个入口点和出口点;
  • 多个协同程序间表现为协作运行,如A的运行过程中需要B的结果才能继续执行。

协程的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。

从另一方面说,协程无法并发其实也将他的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大,所以牢牢掌握线程即可。

自定义range()方法

def my_range(start, stop, step=1):
    while start < stop:
        yield start
        start += 1


g = my_range(0, 3)
print(f'list(g):list(g)')
list(g):[0, 1, 2]

总结

yield:

  1. 提供一种自定义迭代器的方式。
  2. yield可以暂停住函数,并提供当前返回值。

yield和return:

  • 相同点:两者都是在函数内部使用,都可以返回值,并且返回值没有类型和个数的限制
  • 不同点:return只能返回一次值,yield可以返回多次值。

生成器表达式

  • 把列表推导式的[]换成()就是生成器表达式
  • 优点:省内存,一次只产生一个值在内存中
t = (i for i in range(8))
print(t)
print(f'next(t):next(t)')
<generator object <genexpr> at 0x00000256EABFE150>
next(t):0

生成器表达式和列表推导式

列表推导式相当于直接给你一筐蛋,而生成器表达式相当于给你一只老母鸡。

# 生成器表达式
with open('602.txt', 'r', encoding='utf8') as f:
    nums = [len(line) for line in f]

print(max(nums))  # len(william)
7
# 列表推导式
with open('602.txt', 'r', encoding='utf8') as f:
    nums = (len(line) for line in f)

print(max(nums))  # ValueError: I/O operation on closed file.

以上是关于迭代器与生成器的主要内容,如果未能解决你的问题,请参考以下文章

python 基础篇 13 迭代器与生成器

Python迭代器与生成器

第五篇Python之迭代器与生成器

Python迭代器与生成器

Python迭代器与生成器

6Python全栈之路系列之迭代器与生成器