生成器迭代器

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

Python2很多列表的地方,在Python3被更新为生成器
可以基于生成器直接创建列表
filter
map
1、如何创建生成器?
1.1 g1 = (x for x in range(10) if x % 2 == 0)
1.2 g2 = func1()
函数func1中必须包含yield关键字
2、如何生成数据?(下蛋的鸡)
特征:
2.1 必须先生第一个,才能生第二个
2.2 必须是一个一个来
生成语法:
a.next(g)
b.g.__next__()
c.g.send(value)
value 在生成器开始的时候,只能给None
后续可以给随意参数
如果超出生成数据范围,会导致异常 StopIteration

3、生成器的好处?
3.1 时间开销
3.2 内存开销
def generator():
    print("a")
    count = yield 1
    print("---->",count)
    print('b')
    yield 2


g = generator()
print(next(g))

res = g.send('123')
print(res)
print("- -"*10)

lis = [1,2,3,4,5,6,7]
lis1 = [2,3,4,5,6,7,8]
li = list(map(lambda x,y:x,lis,lis1))
for i in li:
    print(i)


g2 = (x for x in range(10) if x % 2 == 0)
print(type(g2))
# 3种使用方式
v = next(g2)
print(v)
v = g2.__next__()
print(v)
v = g2.send(None)
print(v)



import time
import sys
"""
1M = 1024kb
1kb = 1024byte
1byte = 8bit
"""
start = time.time()
# list1 = [x for x in range(10000000)]
list1 = (x for x in range(10000000))
size = sys.getsizeof(list1)#字节
end = time.time()
print(f'耗时:{end-start}秒')
print(f'list1占用内存{size/1024}kb')
# print(f'list1占用内存{size/1024/1024}M') 

 运行结果

 迭代器

  • 迭代器: 拥有__iter__方法和__next__方法的对象就是迭代器   注意:生成器就是迭代器;可迭代对象不一定是迭代器
  • 可迭代性的元素:list, tuple, str, dict, set
  • itertion: 就是迭代,一个接一个(one after another),是一个通用的概念,比如一个循环遍历某个数组。

  • iterable: 这个是可迭代对象,属于python的名词,范围也很广,可重复迭代,满足如下其中之一的都是iterable:

    • 可以for循环: for i in iterable

    • 可以按index索引的对象,也就是定义了__getitem__方法,比如list,str;

    • 定义了__iter__方法。可以随意返回。

    • 可以调用iter(obj)的对象,并且返回一个iterator

  • iterator迭代器对象,也属于python的名词,只能迭代一次。需要满足如下的迭代器协议

    • 定义了__iter__方法,但是必须返回自身

    • 定义了next方法,在python3.x是__next__。用来返回下一个值,并且当没有数据了,抛出StopIteration

    • 可以保持当前的状态

判断是否具有可迭代性(满足可迭代协议):

(1)查看是否实现了__iter__方法

    "__iter__" in dir(可迭代性对象)

(2)判断目标对象是否是可迭代性类实例,使用 isinstance(Iterable)来判断

    from collections.abc import Iterableisinstance(可迭代性对象, Iterable)

for循环的本质

  • 通过__iter__()获取该对象的一个迭代器对象
  • 通过__next__()函数,依次获取下一个元素
class MyRange:
    def __init__(self, stop):
        self.start = 0
        self.stop = stop
        self.step = 1
    
    def __iter__(self):
        while self.start < self.stop:
            yield self.start
            self.start += self.step
    
for i in MyRange(5):
    print(i)

 

迭代器的几个创建方法:

1.生成器函数

 和普通函数的 return 返回不同,生成器函数使用 yield。

>>> def odd_func(start=1, end=10):
...     for val in range(start, end + 1):
...         if val % 2 == 1:
...             yield val
...
>>> of = odd_func(1, 5)
>>> of
<generator object odd_func at 0x101a14200>
>>> iter(of)
<generator object odd_func at 0x101a14200>
>>> next(of)
1
>>> next(of)
3
>>> next(of)
5
>>> next(of)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

2.生成器表达式

>>> g = (v for v in range(1, 5 + 1) if v % 2 == 1)
>>> g
<generator object <genexpr> at 0x101a142b0>
>>> iter(g)
<generator object <genexpr> at 0x101a142b0>
>>> next(g)
1
>>> next(g)
3
>>> next(g)
5
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

3.自定义迭代器

基于模拟Range实现的IntegRange,封装一个类,可以通过for循环直接打印指定范围的质数
# 例如:
# for i in IntegerRange(2,20,1):
# print(i)
# 可以依次打印出2,3,5,7,11...等全为质数的数字,
import math


class IntegerRange:
    def __init__(self, start, end, step):
        self.start = start
        self.end = end
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        while self.start < self.end:
            self.start += self.step
            if self.prime(self.start):
                return self.start
        else:
            raise StopIteration("跑完了")

    def prime(self, n):
        if n <= 1:
            return 0
        for i in range(2, math.ceil(math.sqrt(n + 1))):
            if n % i == 0:
                return 0
        return 1
g = IntegerRange(2,20,1)
print(g.__next__())
print(next(g))

 怎么选择?

到现在为止,我们知道了创建迭代器的 3 种方式,那么该如何选择?

最简单的就是生成器表达式,如果表达式能满足需求,那么就是它;如果需要添加比较复杂的逻辑就选生成器函数;如果前两者没法满足需求,那就自定义类实现吧。总之,选择最简单的方式就行。 

注意:迭代器遍历完一次就不能从头开始了

>>> l = [1, 3, 5]
>>> li = iter(l)
>>> li
<list_iterator object at 0x101a1da90>
>>> 3 in li
True
>>> 3 in li
False

因为 li 是列表迭代器,第一次查找 3 的时候,找到了,所以返回 True,但是由于第一次迭代,已经跳过了 3 那个元素,第二次就找不到了,所以会出现 False。

因此,记得迭代器是「一次性」的。

总结:

  • 实现了迭代器协议的对象都是迭代器
  • 实现了 __iter__() 方法并返回迭代器的对象是可迭代对象
  • 生成器也是一种迭代器
  • 创建迭代器有三种方式,生成器表达式、生成器函数、自定义类,看情况选择最简单的就好
  • 迭代器同时也是可迭代对象
  • 迭代器是「一次性」的
原文地址:https://www.cnblogs.com/songdanlee/p/11191258.html