Python--生成器

生成器

      在python中,如果进行较大数据的存储,如果直接存储在列表之中,则会可能造成内存的不够,与速度的减慢,因为列表创建完是立即创建并存在的,而在python中生成器(generator)能够很大程度上解决这个问题,生成器并不一开始直接创建所有的数据,而是进行一种"惰性的运算",即创建的时候,我们创建的只是一个生成器的对象,里面记录的是方法,而没有生成数据,可以使用特殊的方法,进行随用随取,并且生成器只能遍历一遍
      创建生成器的方法有两种,
      一种是:a = (生成式) 如:test = (a for a in range(10))
在这里插入图片描述
代码解读:
      等号右边表示生成一个生成器,等号表示将右边的生成器赋给等号左边的变量test

      另一种是:在函数中使用yield关键字,使用yield的函数将不再是一个不同的函数,而是一个生成器对象

def test():
    for i in range(3):
        yield i

print(test())

输出结果为:

<generator object test at 0x0000009FA89EAE08> #可能会有所不同,因为运行的环境不同,内存值不一样

获取生成器里面的值

      由于生产器的返回的都是一个对象,所以,要想获取里面的值,必须使用特殊的方法next([generator])、generator.__next__()、for循环。以上的三种方法对以上两种创建的生成器都可以使用。
在这里插入图片描述
不过需要注意的是,在使用next()与__next__()方法的时候,当生成器里面的内容被取完的时候,如果还进行调用以上两种方法,则会报错:
在这里插入图片描述
值得介绍的是for循环的方法,for循环的方法,不管里面的内容有多少个,如果不加以限制的话,都会取到最后一个,而停止不取,防止报错。

#!encoding=utf-8
def test():
    for i in range(3):
        yield i

for e in test():
    print(e)

输出结果为:

0
1
2

yield创建生成器的方法与使用

      首先需要知道的是,使用yield的函数会被默认当作是一个生成器,这个上边已经介绍过了,下面介绍一个yield的特点。
yield相当于集 停止运行、返回值、继续运行 为一体的关键字。

      当生成器第一次取值的时候,会从函数的开始运行,每当运行到yield的代码的时候程序都会停止,并返回yield后面跟着的值,然后再次取值的时候,会返回给函数一个None,替换yield语句(并不是真正的替换,而是将None暂时代替yield代码的位置),并继续从遇到yield停止运行的地方继续进行操作,知道在此遇到yield停止,或者函数结束,一直重复。
如:

#!encoding=utf-8
def test():
    print("-----1-----")
    for i in range(3):
        print("-----2-----")
        ret = yield i
        print(ret)
        print("-----3-----")

b = test()
print("-----第一次取值-----")
print(next(b))
print("-----第二次取值-----")
print(next(b)))
print("-----第三次取值-----")
print(next(b))

输出为:

-----第一次取值-----
-----1-----
-----2-----
0
-----第二次取值-----
None
-----3-----
-----2-----
1
-----第三次取值-----
None
-----3-----
-----2-----
2

代码解读:
      首先创建一个生成器的实例对象,然后第一次取值,从函数开始进行运行,运行到yield,停止,打印返回的值,然后再次取值时,返回给函数yield代码段一个None值,继续运行代码,赋值给ret,打印ret,打印print("-----3-----"),然后循环,再次运行到yield重复停止运行、返回值、继续运行等操作。

yield的send操作

在yield有一个send的操作,用法为generator.send(message),这个方法的与next操作的不同之处在于,message将代替None返回给函数,其余基本都一样,需要注意的是,在进行第一次取值的时候需要将message取值为None。

#!encoding=utf-8
def test():
    print("-----1-----")
    for i in range(3):
        print("-----2-----")
        ret = yield i
        print(ret)
        print("-----3-----")

b = test()
print("-----第一次取值-----")
print(b.send(None))
print("-----第二次取值-----")
print(b.send("我是yield的替代值1"))
print("-----第三次取值-----")
print(b.send("我是yield的替代值2"))

输出结果为:

-----第一次取值-----
-----1-----
-----2-----
0
-----第二次取值-----
我是yield的替代值1
-----3-----
-----2-----
1
-----第三次取值-----
我是yield的替代值2
-----3-----
-----2-----
2

代码解读:
      首先创建一个生成器的实例对象,然后第一次取值,从函数开始进行运行,运行到yield,停止,打印返回的值,然后再次取值时,返回给函数yield代码段message的值,继续运行代码,赋值给ret,打印ret,打印print("-----3-----"),然后循环,再次运行到yield重复停止运行、返回值、继续运行等操作。

生成器的惰性
      生成器是有一个惰性的特点的,也就是说,创建之后,如果去不调用它,它则一直保持原来的状态,只有使用它,问它“要”值的时候才会去运行它。请看下面的示例:

def w1():
    for e in range(1,5):
        yield e
        
g = w1()
for i in [1,10]:
    g = (i+e for e in g)
    
print(list(g))

按照我们之前所学的内容,输出结果应该为:[12,13,14,15]
而实际的输出为:

[21, 22, 23, 24]

代码解读:
      在解释器读取代码的时候,读取到g = w1()创建一个生成器对象,,我们可以设置此时的g= g1=w1(),然后进入for循环遍历,而循环里面只有一条语句,g = (i+e for e in g) ,当第一次遍历的时候,i= 1,此时的g = (i+e for e in g1),我们可以设置此时的g= g2=(i+e for e in g1),接着循环的时候,i= 10,此时的g = (i+e for e in g2),我们可以设置此时的g= g3=(i+e for e in g2),每次循环都是根据之前的g进行再次创建一个生成器,由于生成器惰性的特点,多次创建的生成器只是创建了,并没有去取值,当for循环结束,此时的i=10,当print(list(g))的时候,list语句问最后生成的g要值,也就是取出所有的值,而最后的g又是g3,i为10不变,
在这里插入图片描述
生成器的一次性
      生成器的一次性,是指,一个生成器对象实例,只能完全的取一次值,再次取值,为空或者报错。
如:

#!encoding=utf-8
def w1():
    for e in range(1,5):
        yield e

g = w1()
for i in [1,10]:
    g = (i+e for e in g)

print(list(g))
print(list(g))

输出结果为:

[21, 22, 23, 24]
[]

猜你喜欢

转载自blog.csdn.net/weixin_43442071/article/details/88980657