Maison  >  Article  >  développement back-end  >  Introduction et utilisation des générateurs Python

Introduction et utilisation des générateurs Python

零下一度
零下一度original
2017-07-19 23:25:521787parcourir

Le générateur en python enregistre l'algorithme, et la valeur ne sera calculée que lorsque la valeur est vraiment nécessaire. C’est une évaluation paresseuse.

Il existe deux manières de créer un générateur.

La première méthode : Remplacez le [] d'une expression de génération de liste par () pour créer un générateur :

>>> x pour x dans la plage (10)]>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> * x pour x dans la plage (10)) # Notez qu'après avoir remplacé [] par (), au lieu de générer un tuple, un objet générateur>>> >

Deuxième méthode : utilisez le mot-clé rendement dans la fonction, et la fonction devient un générateur.

Une fois qu'il y a un rendement dans la fonction, l'exécution s'arrêtera lorsque le rendement sera atteint, et le calcul ne continuera que lorsque d'autres calculs seront nécessaires. Donc, peu importe même si la fonction génératrice a une boucle infinie, elle calculera autant qu'elle doit compter, et elle ne comptera plus si ce n'est pas nécessaire.

def fib() :

a, b = 0, 1

while True :
donne a
a, b = b, a + b
f = fib()
imprimer f, next(f), next(f), next(f)
# a97a63271323add60800053c4885edc5 🎜>
Comme dans l'exemple ci-dessus, lorsque f est généré pour la première fois, c'est un générateur, et chaque fois que la prochaine exécution est exécutée, il s'exécutera pour produire a.

Bien sûr, next() est rarement utilisé. On peut utiliser directement une boucle for pour parcourir un générateur. En fait, l'implémentation interne de la boucle for consiste à appeler en permanence next().

Le générateur peut éviter les calculs inutiles et améliorer les performances ; il économise également de l'espace et peut réaliser des structures de données en boucle infinie (infinies).

Syntaxe du générateur

Expression du générateur : identique à la syntaxe d'analyse de liste, mais remplacez le [] de l'analyse de liste par ()

L'expression du générateur peut faire L'analyse de liste peut essentiellement gérer choses, mais lorsque la séquence à traiter est relativement grande, l'analyse de liste consomme plus de mémoire.


Fonction génératrice : Si le mot-clé rendement apparaît dans une fonction, alors la fonction n'est plus une fonction ordinaire, mais une fonction génératrice.

En Python, le rendement est un tel générateur.

Le mécanisme de fonctionnement du générateur de rendement :

Lorsque vous demandez un numéro au générateur, le générateur s'exécutera jusqu'à ce que l'instruction de rendement apparaisse, et le générateur les paramètres vous sont donnés, puis le générateur ne continuera pas à fonctionner. Lorsque vous lui demanderez le numéro suivant, il repartira du dernier état. Commencez à exécuter jusqu'à ce que l'instruction de rendement apparaisse, donnez-vous les paramètres, puis arrêtez. Ceci est répété jusqu'à ce que la fonction se termine.

Utilisation de Yield :

En Python, lorsque vous définissez une fonction et utilisez le mot-clé Yield, la fonction est un générateur, et son exécution sera la même que les autres fonctions ordinaires. de nombreuses différences. La fonction renvoie un objet, au lieu d'obtenir la valeur du résultat comme l'instruction return que vous utilisez habituellement. Si vous voulez obtenir la valeur, vous devez appeler la fonction next()

Prenons Fibonacci comme exemple :

#coding:utf8
def fib(max): #10
    n, a, b = 0, 0, 1
    while n < max: #n<10
        #print(b)
        yield b
        a, b = b, a + b

        n += 1
    return 

f = fib(10)
for i in f:
    print f
D'après la description ci-dessus du mécanisme de fonctionnement, nous pouvons savoir que lorsque le programme atteint la ligne de rendement, il ne continuera pas à s'exécuter. Au lieu de cela, il renvoie un objet itérateur

qui contient l'état

de tous les paramètres de la fonction actuelle. Le but est de lorsqu'elle est appelée pour la deuxième fois, toutes les valeurs de paramètres​​de la fonction sont accessibles aux valeurs​​lors du premier accès, au lieu de réaffecter les valeurs. Lorsque le programme est appelé pour la première fois :

程序第二次调用时:

从前面可知,第一次调用时,a,b=0,0,那么,我们第二次调用时(其实就是调用第一次返回的iterator对象的next()方法),程序跳到yield语句处,

执行a,b = b, a+b语句,此时值变为:a,b = 0, (0+1) => a,b = 0, 1

程序继续while循环,当然,再一次碰到了yield a 语句,也是像第一次那样,保存函数所有参数的状态,返回一个包含这些参数状态的iterator对象。

等待第三次的调用....

 

通过上面的分析,可以一次类推的展示了yield的详细运行过程了!

通过使用生成器的语法,可以免去写迭代器类的繁琐代码,如,上面的例子使用迭代类来实现,代码如下:

#coding:UTF8

class Fib:  
    def __init__(self, max):  
        self.max = max
        print self.max
    def __iter__(self):  
        self.a = 0  
        self.b = 1 
        self.n = 0 
        return self  
    def next(self):  
        fib = self.n  
        if fib >= self.max:  
            raise StopIteration  
        self.a, self.b = self.b, self.a + self.b  
        self.n += 1
        return self.a
    
f = Fib(10)
for i in f:
    print i

yield 与 return

在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration;

 

 如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

如果在return后返回一个值,会直接报错,生成器没有办法使用return来返回值。

 

生成器支持的方法(借鉴别人的例子,感觉蛮好的)

     close(...)
 |      close() -> raise GeneratorExit inside generator.
 |  
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
 |  
 |  send(...)
 |      send(arg) -> send &#39;arg&#39; into generator,
 |      return next yielded value or raise StopIteration.
 |  
 |  throw(...)
 |      throw(typ[,val[,tb]]) -> raise exception in generator,
 |      return next yielded value or raise StopIteration.

 

close()

手动关闭生成器函数,后面的调用会直接返回StopIteration异常。

#coding:UTF8

def fib():
    yield 1
    yield 2
    yield 3

f = fib()
print f.next()
f.close()
print f.next()

 

send()

生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。
这是生成器函数最难理解的地方,也是最重要的地方,

def gen():
    value=0
    while True:
        receive=yield value
        if receive==&#39;e&#39;:
            break
        value = &#39;got: %s&#39; % receive
 
g=gen()
print(g.send(None))     
print(g.send(&#39;aaa&#39;))
print(g.send(3))
print(g.send(&#39;e&#39;))

执行流程:

  1. 通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置。此时,执行完了yield语句,但是没有给receive赋值。yield value会输出初始值0注意:在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。

  2. 通过g.send(‘aaa’),会传入aaa,并赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句有停止。此时yield value会输出”got: aaa”,然后挂起。

  3. 通过g.send(3),会重复第2步,最后输出结果为”got: 3″

  4. 当我们g.send(‘e’)时,程序会执行break然后推出循环,最后整个函数执行完毕,所以会得到StopIteration异常。

最后的执行结果如下:

0
got: aaa
got: 3
Traceback (most recent call last):
  File "1.py", line 15, in <module>
    print(g.send(&#39;e&#39;))
StopIteration

 

throw()

用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。
throw()后直接跑出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。

def gen():
    while True: 
        try:
            yield &#39;normal value&#39;
            yield &#39;normal value 2&#39;
            print(&#39;here&#39;)
        except ValueError:
            print(&#39;we got ValueError here&#39;)
        except TypeError:
            break
 
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))

执行流程:

  1. print(next(g)):会输出normal value,并停留在yield ‘normal value 2’之前。

  2. 由于执行了g.throw(ValueError),所以会跳过所有后续的try语句,也就是说yield ‘normal value 2’不会被执行,然后进入到except语句,打印出we got ValueError here。然后再次进入到while语句部分,消耗一个yield,所以会输出normal value。

  3. print(next(g)),会执行yield ‘normal value 2’语句,并停留在执行完该语句后的位置。

  4. g.throw(TypeError):会跳出try语句,从而print(‘here’)不会被执行,然后执行break语句,跳出while循环,然后到达程序结尾,所以跑出StopIteration异常。

  

 

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn