代码改变世界

python -- 生成器

  abce  阅读(344)  评论(0编辑  收藏  举报

在for循环中,每次yield值后,控制权就返回给for循环

生成器类似于返回一个数组的函数。生成器有参数、可以被调用,并生成值的序列。和函数一次返回整个数组不同,生成器每次只是生成一个值,这样会占用很少的内存,并且调用者可以立即处理生成的值。概括来说,生成器看起来像个函数,但是使用起来像个迭代器。

Python提供的,在需要时才生成结果的工具:
-生成器函数:
  使用def定义,但是每次使用yield生成返回值值、挂起、在继续运行。
-生成器表达式:
  类似于列表推导,但不是创建一个结果列表,而是每次根据需要生成对象。

因为无论是生成器函数、还是生成器表达式都不是一次生成一个结果列表,这样可以节省内存空间,并将计算时间以迭代协议的方式切分开。

 

生成器函数:yield和return
生成器函数和常规函数有点类似,都是使用def定义。当创建后,自动实现迭代协议。
常规函数会返回值并退出。而迭代器函数返回一个值后会自动挂起、然后再次执行。
生成器函数和常规函数之间的主要区别是前者yield一个值,后者返回一个值。yield会挂起函数,返回一个值给调用者。

生成器是和迭代协议绑在一起的。可迭代的对象定义一个方法:__next__(),该方法要么返回迭代器的下一个值,要么抛出一个异常。

定义生成器的时候需要使用关键字:yield。

让我们来看个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> def counter(n):
    print("counter()")
    while True:
        yield n
        print("increment n")
        n += 1
 
         
>>> c = counter(2)
>>> c
<generator object counter at 0x000000000246D480>
>>> next(c)
counter()
2
>>> next(c)
increment n
3
>>> next(c)
increment n
4
>>> c.next()
increment n
5
>>> c.next()
increment n
6
>>>

1.关键字yield表明该函数不是一个常规函数,而是一个迭代器函数。
2.生成一个迭代器的实例,和调用常规函数类似。但是调用的时候并不真正执行函数代码。
3.counter()返回一个迭代器对象


比如,下面的迭代器生成数字的立方值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> def cubic_generator(n):
    for i in range(n):
        yield i**3
 
         
>>> cg = cubic_generator(3)
>>> cg
<generator object cubic_generator at 0x0000000002A72CF0>
>>> cg.next()
0
>>> cg.next()
1
>>> cg.next()
8
>>> cg.next()
 
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    cg.next()
StopIteration
>>>

在for循环中,每次yield值后,控制权就返回给for循环:

1
2
3
4
5
6
7
8
9
10
>>> for i in cubic_generator(5):
    print(i)
 
     
0
1
8
27
64
>>>

如果用return代替yield:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> def cubic_generator(n):
    for i in range(n):
        return i**3
 
     
>>> for i in cubic_generator(5):
    print(i)
 
     
 
Traceback (most recent call last):
  File "<pyshell#54>", line 1, in <module>
    for i in cubic_generator(5):
TypeError: 'int' object is not iterable
>>> cubic_generator(5)
0
>>>

使用生成器的示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> def fib():
    limit = 10
    count = 0
    a,b = 0,1
    while True:
        yield a
        a,b = b,a+b
        if (count == limit):
            break
        count += 1
 
         
>>> for i in fib():
    print(i)
 
     
0
1
1
2
3
5
8
13
21
34
55
>>>

  

使用生成器实现斐波纳契数列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> def fib(max):
    a,b = 0,1
    while a < max:
        yield a
        a,b = b,a+b
 
         
>>> for i in fib(500):
    print (i)
 
     
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
>>> list(fib(500))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
>>>

  

 

生成器表达式: 可推导的迭代器

迭代器和列表推导相结合,形成了一个新特性:生成器表达式。
生成器表达式和列表表达式类似,但是前者被小括号包含着,后者是用方括号包含的。

1
2
3
4
5
6
7
8
9
10
11
>>> #列表推导
>>> [x**3 for x in range(5)]
[0, 1, 8, 27, 64]
>>>
>>> #生成器表达式
>>> (x**3 for x in range(5))
<generator object <genexpr> at 0x0000000002BE0798>
>>>
>>> list(x**3 for x in range(5))
[0, 1, 8, 27, 64]
>>>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> gen=(x**3 for x in range(5))
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
8
>>> gen.next()
27
>>> gen.next()
64
>>> gen.next()
 
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    gen.next()
StopIteration
>>>

 

生成器:函数 vs 表达式

相同的迭代可以用生成器函数或生成器表达式实现。二者都可以自动迭代或手动迭代。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> gen = (c*5 for c in 'pyhton')
>>> list(gen)
['ppppp', 'yyyyy', 'hhhhh', 'ttttt', 'ooooo', 'nnnnn']
>>>
>>> def gen(x):
    for c in x:
        yield c*5
 
         
>>> g=gen('python')
>>> list(g)
['ppppp', 'yyyyy', 'ttttt', 'hhhhh', 'ooooo', 'nnnnn']
>>>

 

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示