Python 核心特性之生成器与迭代器

生成器

通过列表生成式(列表推导式),我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。

而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的 list,从而节省大量的空间。

在Python中,这种一边循环一边计算的机制,称为生成器: generator

优点: 需要新的数字,就生成一个新的,不同于列表,需要一下子全出来,更占用内存。

通过以下实例代码可看出其实际的时间消耗和内存消耗:

import time
import sys


# 通过 for 循环创建列表
start = time.time()
list1 = []
for x in range(10000000):
    list1.append(x)
end = time.time()
l_time = end - start

print(f'通过 for 循环创建列表时间开销:{l_time}')
print(f'通过 for 循环创建列表内存开销:{sys.getsizeof(list1)}')

# 通过列表推导式创建列表
time.clock()
list2 = [x for x in range(10000000)]
l_time = time.clock()

print(f'通过列表推导式创建列表时间开销:{l_time}')
print(f'通过列表推导式创建列表内存开销:{sys.getsizeof(list2)}')

# 创建生成器
time.clock()
g = (x for x in range(10000000))
g_time = time.clock()

print(f'创建生成器时间开销:{g_time}')
print(f'创建生成器内存开销:{sys.getsizeof(g)}')


运行结果:

通过 for 循环创建列表时间开销:1.5724666118621826
通过 for 循环创建列表内存开销:81528056
通过列表推导式创建列表时间开销:0.7601992
通过列表推导式创建列表内存开销:81528056
创建生成器时间开销:0.7602516
创建生成器内存开销:88

得到生成器的方式

  • 通过列表推导式的形式得到生成器
g = (x for x in range(0, 1000000, 2))

print(type(g))

# 运行结果:
<class 'generator'>
  • 通过定义函数时使用 yield 关键字得到生成器
def create_generator():
    for x in range(0, 1000000, 2):
        yield x


g = create_generator()
print(type(g))

# 运行结果:
<class 'generator'>

得到生成器中元素的方式

  • 通过调用 __next__() 方法得到生成器中的元素
g = (x for x in range(0, 1000000, 2))

print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

# 运行结果:
0
2
4
6
  • 调用系统内置方法 next(生成器对象)
g = (x for x in range(0, 1000000, 2))

print(next(g))
print(next(g))
print(next(g))
print(next(g))

# 运行结果:
0
2
4
6
  • 通过调用 send() 方法获取元素
def create_generator():
    for x in range(0, 1000000, 2):
        temp = yield x
        print(temp)


g = create_generator()
print(next(g))
print(g.send('aaa'))

# 运行结果:
0
aaa
2

注意:首次调用获取生成器元素不传参或者传递非None参数都会导致异常
解决方案:

  • 首次调用使用__next__(),不使用 send()
  • 首次使用 send(None)
  • 使用 for 循环遍历生成器
g = (x for x in range(0, 10, 2))

for i in g:
    print(i, end='\t')

# 运行结果:
0	2	4	6	8	

迭代器

迭代的概念

  • 迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
  • 迭代器只能往前不会后退。
  • 可以被 next() 函数调用并不断返回下一个值的对象称为迭代器:Iterator

可迭代对象(能够用于 for 循环的对象)

  • 生成器
  • 元组、列表、集合、字典、字符串

如何判断一个对象是否是可迭代?

调用系统内置方法:isinstance(对象, 类)进行判断:

from collections import Iterable


def example():
    list_ = [1, 4, 3, 6]
    g = (x + 1 for x in range(10))

    print(isinstance(list_, Iterable))
    print(isinstance(g, Iterable))
    print(isinstance('abc', Iterable))
    print(isinstance(100, Iterable))


if __name__ == '__main__':
    example()
    
# 运行结果:
True
True
True
False

迭代器

  • 可迭代的不一定是迭代器。
  • 在所有可以用于 for 循环的对象中,迭代器可以被 next() 函数不断调用并生成下一个值,直到抛出 StopIteration 异常表示无法继续为止。
  • 像生成器这种可以被 next() 函数调用并不断生成下一个值的对象被称为迭代器(Iterator)
  • 可通过 iter() 函数转换成迭代器:可以使用 for 循环遍历迭代器,也可以使用 next() 函数 或者 __next()__ 方法访问迭代器。
from collections import Iterator


# 创建列表
list_ = [x for x in range(0, 10, 2)]

# 创建生成器
g = (x for x in range(0, 10, 2))

# 判断列表是否为迭代器
print(isinstance(list_, Iterator))

# 判断生成器是否为迭代器
print(isinstance(g, Iterator))

# 列表调用 iter() 转换成迭代器
list_iterable = iter(list_)
print(isinstance(list_iterable, Iterator))

for i in list_iterable:
    print(i, end='\t')

# 迭代器无法生成下一个值报 StopIteration 异常
list_iterable.__next__()


运行结果:

Traceback (most recent call last):
  File "E:/PythonProject/test3.py", line 24, in <module>
    list_iterable.__next__()
StopIteration
False
True
True
0	2	4	6	8	

猜你喜欢

转载自blog.csdn.net/qq_44214671/article/details/110757582