Python生成器

生成器
	
	經過列表生成式,咱們能夠直接建立一個列表。可是受內存限制,列表容量確定是有限的。
	並且,建立一個不少不少元素的列表,不只佔用很大的內存空間,若是咱們僅僅訪問前幾
	元素,那後面絕大多數的元素佔用的空間就白白浪費了。

	因此,若是列表元素能夠按照某種算法推算出來,那多好!這樣就沒必要浪費空間了,這樣
	你好我好你們好。

	在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator

	要建立一個generator,有不少種方法。
		1)只要把一個列表生成式的[]改成(),就建立了一個generator。
			L = [i * i for i in range(5)]
			print(L)#[0, 1, 4, 9, 16]

			g = (i * i for i in range(5))
			print(g)#<generator object <genexpr> at 0x111136ca8>

			建立L和g的區別僅在於最外層[]和(),L是一個list而g是一個genratot。

			咱們能夠直接打印出list的每個元素,但咱們怎麼打印出generator的每個元素呢?
			咱們能夠經過  next(g)挨個拿其中的元素,最後一個取完以後再進行 next()操做會
			報錯(StopIteration)。
			一個一個取是在太繁瑣,咱們能夠用for循環,由於genertor是一個可迭代對象。
			g = (i * i for i in range(5))
			for i in g:
				print(i) #用for循環來迭代,而且不須要關心StopIteration報錯。

		2)用函數來實現。
			好比,斐波那契數列:1,1,2,3,5,8,13,21,34....
			用列表生成式寫不出來,可是咱們能夠用函數把它打印出來:
			def fib(number):
			    n, a, b = 0, 0, 1
			    while n < 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)改成 yield b就能夠了:
			def fib(number):
			    n, a, b = 0, 0, 1
			    while n < number:
			        yield b
			        a, b = b, a + b
			        n = n + 1
			    return 'OK!'

			print(fib(5))#<generator object fib at 0x105606ca8>
			注意:
				這裏難理解的就是generator和函數的執行流程是不同的。
				函數是順序執行,遇到return語句或者最後一行函數語句就
				返回。
                注意:函數建立一次生成一個生成器,因此咱們會將建立的生
                    成器賦值給一個變量。若是直接用函數自己這個生成器,
                    咱們沒用一次生成一個新的生成器對象,因此,咱們一
                    般都將建立的生成器賦給一個變量。
				generetor的函數,在每次調用 next()的時候執行,遇到
				yield語句返回,再次執行時從上次返回的yield語句處繼續
				執行。

				例子:
					def odd():
					    print('step 1')
					    yield 1
					    print('step 2')
					    yield(3)
					    print('step 3')
					    yield(5)

					f = odd()
					print(next(f))
					print(next(f))
					print(next(f))

					結果:
						step 1
						1
						step 2
						3
						step 3
						5
					執行三次後再執行 next(f) 就會報錯了。
					能夠看到odd不是普通函數,而是generator遇到yield就會中斷
					下次又繼續執行,執行三次後已經沒有yield能夠執行,因此再執
					行 next(f) 就會報錯了。最後的yield後面通常不寫東西。


			把函數改成generator後,咱們基本不這麼用 next()來獲取下一個返回值
			而是直接使用for循環來迭代。
			好比上面的fib函數。

				def fib(number):
				    n, a, b = 0, 0, 1
				    while n < number:
				        yield b
				        a, b = b, a + b
				        n = n + 1
				    return 'OK!'

				for i in fib(5):
				    print(i)
				結果:
					1
					1
					2
					3
					5

				注意:用for循環調用generator時,發現拿不到generator的return
					語句的返回值。若是想要拿到返回值,必須捕獲StopIteration錯誤
					返回值包含在StopIteration的value中。

					(關於如何捕獲,異常處理見。)


    接下來咱們來看send⽅方法, send和next()同樣均可以讓生成器執行到下一個yield.
        例子;
            def eat():
                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>
            def add(a, b):
                return a+b
            def Test():
                for i in range(4):
                    yield i
            g = Test()
            for n in [2, 10]:
                g = (add(n, i) for i in g)
            print(list(g))
                
            結果:
                [20, 21, 22, 23]
            分析:
                print(list(g))這個語句以前都是準備階段,只程序走到
                print(list(g))這個語句這裏程序纔會執行,那麼在此程
                序執行的準備階段前面作了什麼事呢:
                    到for循環這
                        n = 2
                        g = (add(n, i) for i in Test())
                        n = 10
                        g = (add(n, i) for i in add(n, i) for i in Test())
                        這個時候執行list(g)其實執行的只是n==10的時候
                        結果:[20, 21, 22, 23]

            <2>
            def func():
                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>
            def func():
                print(111)
                yield 222
            g = func() # 生成器g
            g1 = (i for i in g) # 生成器g1. 可是g1的數據來源於g
            g2 = (i for i in g1) # 生成器器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
相關文章
相關標籤/搜索