首页 > 技术文章 > Python的generator生成器

Ting-light 2018-08-27 17:29 原文

generator保存的是算法,元素仅在使用的时候生成,占用内存小,总元素的个数可以是无限个。

简单的生成器与列表生成式,区别仅在于将中括号[ ],换成小圆括号( ).

In [1]: g=(x*x+2 for x in range(5))

 

In [2]: g

Out[2]: <generator object <genexpr> at 0x000002A3965CED00>

 

In [3]: next(g)

Out[3]: 2

 

In [4]: next(g)

Out[4]: 3

可以通过next()不断获得它的下一个返回值,直到没有元素了就会抛出StopIteration错误,一般情况下,会使用for语句循环。

for i in g:

    print(i)

 

generator每次生成新的元素,都会根据上一个元素结束时的状态来生成,非常适合需要递归的算法,下一个元素根据上一个元素时的状态推演而出。

 

generator函数:如果一个函数中包含yield关键字,那它就是一个生成器函数。

 

 

generator函数的,在调用next()时执行,先正常执行,遇到yield返回yield后面的表达式,下次调用next()从yield处开始继续执行,直到元素全部返回完或遇到return 就会抛出StopIteration错误终止。如果generator函数中有return的值需要获取,可以通过捕获错误,输出错误的值来获取:

except StopIteration as e:

    print(e.value)

 

 

使用generator求解8(N)皇后的所有解法 的算法:

def conflict(state,nextX):

    """判断当前选定的X坐标是否与之前的状态冲突"""

    nextY=len(state)    

    for i in range(nextY):

        if abs(state[i]-nextX)in(0,nextY-i): 

            return True    

    return False

 

def queens(num=8,state=()):

 """

返回值是在当前状态state下,剩下的num-len(state)行每行的X坐标组成的元组"""

    if len(state)>num-1:

        raise ValueError ("len(state) cann\'t be bigger than num-1" )

    #尝试X的坐标值

    for pos in range(num):

        #如果相对于当前状态,下一行的x坐标不冲突

        if not conflict(state,pos):   

            #如果剩下的需确定X坐标的行数num-len(state)为1,则返回该 单个X值的元组         

            if len(state)==num-1:       

                yield(pos,)            

            else:  

                #如果返回需返回X坐标的行数大于1,就返回(pos,)+【确定该行X 值得状态下,剩下num-len(state+(pos,))行每行X坐标组成的元组】,即(pos,)+result                

                for result in queens(num,state+(pos,)):                    

                    yield (pos,)+result

 

print(len(list(queens(8))),'*8**9*',len(list(queens(9))))

fh=queens(4)

for i in fh:    

    print(i)      

   此递归形式类似如下:

queens(n,state0)    =   pos1+queens(n,state1)

queens(n,state1)    =  pos2+queens(n,state2)

queens(n,state2)    =  pos3+queens(n,state3)

...

queens(n,staten-1)

=posn

                                                                                                                                                                                           由于queens(n,state1)的值可能有多个,而pos1的值也可能有多个,在递归式中只是选用了其中一个,故算法中使用了两个for循环。

yiled后面的返回表达式(pos,)+result  中result的值需要计算(计算过程就形同上面的递归过程,计算完后则返回)

 

当前状态的解较复杂,但可以等于F(下一个状态下的解)  F为一元函数,然后一直递归到最单一状态下的一个简单解时,就可以使用生成器的yield F(x+1) ,逐渐递归到 F(x+n)  ,  F(x+n)的值是固定

仅通过简单语句 

def f(x):

if x=X+n:

    yield 固定值

else:

    yield F( f(x+1) )

上述程序中,如果满足条件的pos,和queens(num,state+(pos,))都只有一个,则程序中只有两个yield,则queens(num,state)最多返回两个元素。

 

突然想到上述递归,也可以采用return F(f(x+1))的方式来实现,但是

yield与return的区别在于,return 计算完其后得到表达式,函数就结束; 而yield 计算完其后的表达式,还可以继续返回第二个yield后面的表达式的值,直到所有的代码都运行完毕。

 

另外

普通函数调用会直接返回结果,而generator调用会返回一个generator实例,要通过for或next()获取其值:

f=abs(-4)

4

推荐阅读