美文网首页
Python生成器

Python生成器

作者: 小学弟_ | 来源:发表于2018-07-18 14:56 被阅读0次

生成器

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

而且,创建一个很多很多元素的列表,不仅占用很大的内存空间,如果我们仅仅访问前几

元素,那后面绝大多数的元素占用的空间就白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那多好!这样就不必浪费空间了,这样

你好我好大家好。

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

要创建一个generator,有很多种方法。

1)只要把一个列表生成式的[]改为(),就创建了一个generator。

L = [i * iforiinrange(5)]

print(L)#[0, 1, 4, 9, 16]

g = (i * iforiinrange(5))

print(g)# at 0x111136ca8>

创建L和g的区别仅在于最外层[]和(),L是一个list而g是一个genratot。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

我们可以通过  next(g)挨个拿其中的元素,最后一个取完之后再进行 next()操作会

报错(StopIteration)。

一个一个取是在太繁琐,我们可以用for循环,因为genertor是一个可迭代对象。

g = (i * iforiinrange(5))

foriing:

print(i) #用for循环来迭代,并且不需要关心StopIteration报错。

2)用函数来实现。

比如,斐波那契数列:1,1,2,3,5,8,13,21,34....

用列表生成式写不出来,但是我们可以用函数把它打印出来:

deffib(number):

    n, a, b =0,0,1

    whilen < number:

        print(b)

        a, b = b, a + b

        n = n +1

    return'OK!'

print(fib(5))

结果:

1

1

2

3

5

OK!

我们可以看出从第一个元素开始,推算出后续任意的元素。很像generator。

要把fib函数变成generator,只需要把print(b)改为yieldb就可以了:

deffib(number):

    n, a, b =0,0,1

    whilen < number:

        yieldb

        a, b = b, a + b

        n = n +1

    return'OK!'

print(fib(5))#

注意:

这里难理解的就是generator和函数的执行流程是不一样的。

函数是顺序执行,遇到return语句或者最后一行函数语句就

返回。

                注意:函数创建一次生成一个生成器,所以我们会将创建的生

                    成器赋值给一个变量。如果直接用函数本身这个生成器,

                    我们没用一次生成一个新的生成器对象,所以,我们一

                    般都将创建的生成器赋给一个变量。

generetor的函数,在每次调用 next()的时候执行,遇到

yield语句返回,再次执行时从上次返回的yield语句处继续

执行。

例子:

defodd():

    print('step 1')

    yield1

    print('step 2')

    yield(3)

    print('step 3')

    yield(5)

f = odd()

print(next(f))

print(next(f))

print(next(f))

结果:

step1

1

step2

3

step3

5

执行三次后再执行 next(f) 就会报错了。

可以看到odd不是普通函数,而是generator遇到yield就会中断

下次又继续执行,执行三次后已经没有yield可以执行,所以再执

行 next(f) 就会报错了。最后的yield后面一般不写东西。

把函数改为generator后,我们基本不这么用 next()来获取下一个返回值

而是直接使用for循环来迭代。

比如上面的fib函数。

deffib(number):

    n, a, b =0,0,1

    whilen < number:

        yieldb

        a, b = b, a + b

        n = n +1

    return'OK!'

foriinfib(5):

    print(i)

结果:

1

1

2

3

5

注意:用for循环调用generator时,发现拿不到generator的return

语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误

返回值包含在StopIteration的value中。

(关于如何捕获,异常处理见。)

    接下来我们来看send⽅方法, send和next()一样都可以让生成器执行到下一个yield.

        例子;

            defeat():

                print("我吃什么啊")

                a =yield"馒头"

                print("a=",a)

                b =yield"⼤大饼"

                print("b=",b)

                c =yield"韭菜盒⼦子"

                print("c=",c)

                yield"GAME OVER"

            gen = eat()# 获取⽣生成器器

            ret1 = next(gen)

            print(ret1)

            ret2 = gen.send("胡辣汤")

            print(ret2)

            ret3 = gen.send("狗粮")

            print(ret3)

            ret4 = gen.send("猫粮")

            print(ret4)

            结果:

                我吃什么啊

                馒头

                a= 胡辣汤

                ⼤大饼

                b= 狗粮

                韭菜盒⼦子

                c= 猫粮

                GAME OVER

        send和next()区别:

            1.send()和next()都是让生成器向下走一次

            2.send可以给上一个yield的位置传递值,不能给最后一个yield发送值.

                在第一次执行生成器代码的时候不能使用send()

        三道题:

            <1>

            defadd(a, b):

                returna+b

            defTest():

                foriinrange(4):

                    yieldi

            g = Test()

            fornin[2,10]:

                g = (add(n, i)foriing)

            print(list(g))

            结果:

                [20,21,22,23]

            <2>

            deffunc():

                print('1')

                yield 'This is one step'

                print('2')

                yield 'This is two step'

                print('3')

                yield 'This is theree step'

            it = func()#函数返回生成器

            print(list(it))

            结果:

            1

            2

            3

            ['This is one step', 'This is two step', 'This is theree step']

            <3>

            deffunc():

                print(111)

                yield222

            g = func()# 生成器g

            g1 = (iforiing)# 生成器g1. 但是g1的数据来源于g

            g2 = (iforiing1)# 生成器器g2. 来源g1

            print(list(g)) # 获取g中的数据. 这时func()才会被执行. 打印111.获取到222. g完毕.

            print(list(g1)) # 获取g1中的数据. g1的数据来源是g. 但是g已经取完了了. g1 也就没有数据了

            print(list(g2))# 和g1同理

            结果:

                111

                [222]

                []

                []

    Form zero to hero

相关文章

网友评论

      本文标题:Python生成器

      本文链接:https://www.haomeiwen.com/subject/uibopftx.html