Maison  >  Article  >  développement back-end  >  Introduction à l'utilisation de rendement en Python

Introduction à l'utilisation de rendement en Python

黄舟
黄舟original
2017-05-21 13:55:471377parcourir

Cet article présente principalement une brève analyse de l'utilisation de Python rendement. L'éditeur pense que c'est assez bon, je vais donc le partager avec vous maintenant et le donner comme référence. Suivons l'éditeur pour voir comment générer la séquence de Fibonacci


La séquence de Fibonacci est une séquenceRécursivetrès simple, sauf pour le premier et le deuxième nombres, n'importe quel nombre peut être obtenu en additionnant les deux premiers nombres. Utiliser un programme informatique pour générer les N premiers nombres de la séquence de Fibonacci est un problème très simple. De nombreux débutants peuvent facilement écrire la fonction suivante :

Listing 1. Sortie simple des N premiers nombres de la séquence de Fibonacci

 def fab(max): 
  n, a, b = 0, 0, 1 
  while n < max: 
    print b 
    a, b = b, a + b 
    n = n + 1

En exécutant fab(5), nous pouvons obtenir la sortie suivante :

>> ;> ; fab(5)
1
1
2
3
5

Le résultat ne pose aucun problème, mais les développeurs expérimentés le feront remarquer directement en utilisant print imprimer des nombres dans la fonction fab rendra la fonction moins réutilisable, car la fonction fab renvoie None et les autres fonctions ne peuvent pas obtenir la séquence générée par la fonction.

Pour améliorer la réutilisabilité de la fonction fab, il est préférable de ne pas imprimer la séquence directement, mais de renvoyer une Liste. Ce qui suit est la deuxième version réécrite de la fonction fab :

Listing 2. Afficher les N premiers nombres de la séquence de Fibonacci Deuxième version

 def fab(max): 
  n, a, b = 0, 0, 1 
  L = [] 
  while n < max: 
    L.append(b) 
    a, b = b, a + b 
    n = n + 1 
  return L

Vous pouvez utiliser la méthode suivante pour imprimer la liste renvoyée par la fonction fab :

>>> 
for
 n in fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

La fonction fab réécrite peut répondre aux exigences de réutilisabilité en renvoyant une liste, mais les développeurs plus expérimentés le feront. a souligné que la mémoire occupée par cette fonction pendant le fonctionnement augmentera à mesure que le paramètre max augmentera. Si vous souhaitez contrôler l'occupation de la mémoire, il est préférable de ne pas utiliser List pour enregistrer les résultats intermédiaires, mais d'utiliser un objet Itérer. Par exemple, en Python2 http://www.php.cn/wiki/1078.html" target="_blank">range(1000): pass entraînera l'affichage d'une liste de 1000 éléments. généré, et le code :

ne générera pas une liste de 1000 éléments, mais renverra la valeur suivante à chaque itération, occupant très peu d'espace mémoire. Parce que xrange ne renvoie pas une liste, mais un objet itérable.

En utilisant iterable, nous pouvons réécrire la fonction fab dans une

class for i in <a href="http://www.php.cn/wiki/1078.html" target="_blank">range</a>(1000): pass qui prend en charge iterable :

 for i in xrange(1000): passListing 4. La troisième version

La classe Fab renvoie continuellement le numéro suivant dans la séquence via next(), et l'utilisation de la mémoire est toujours constante :

Cependant, le code de cette version réécrite en utilisant class est bien moins concis que la première version de la fonction fab. Yield est pratique si nous voulons conserver la simplicité de la première version de la fonction fab tout en obtenant des effets itérables :

class Fab(object): 

  def init(self, max): 
    self.max = max 
    self.n, self.a, self.b = 0, 0, 1 

  def iter(self): 
    return self 

  def next(self): 
    if self.n < self.max: 
      r = self.b 
      self.a, self.b = self.b, self.a + self.b 
      self.n = self.n + 1 
      return r 
    raise StopIteration()
Listing 5. La quatrième version utilisant rendement


 >>> for n in Fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5
Par rapport à la première version, la quatrième version de fab ne change que print b pour rendement b, ce qui permet d'obtenir l'effet itérable tout en conservant la simplicité.


L'appel de la quatrième version de fab est exactement le même que celui de la deuxième version de fab :

Pour faire simple, la fonction de rendement est de tourner une fonction dans un générateur, la fonction avec rendement n'est plus une fonction ordinaire. L'interpréteur Python la traitera comme un générateur. L'appel de fab(5) n'exécutera pas la fonction fab, mais renverra un objet itérable ! Lorsque la boucle for
 def fab(max): 
  n, a, b = 0, 0, 1 
  while n < max: 
    yield b 
    # print b 
    a, b = b, a + b 
    n = n + 1 

&#39;&#39;&#39;

est exécutée, chaque boucle exécutera le code à l'intérieur de la fonction fab. Lorsqu'elle atteint le rendement b, la fonction fab renvoie une valeur d'itération. Dans l'itération suivante, le code commence à partir de l'instruction suivante. de rendement b. L'exécution continue et la
variable locale

de la fonction est exactement la même qu'avant la dernière interruption, donc la fonction continue de s'exécuter jusqu'à ce qu'elle rencontre à nouveau rendement.


Vous pouvez également appeler manuellement la méthode next() de fab(5) (car fab(5) est un objet générateur, qui a une méthode next()), afin que nous puissions en voir plus clairement Processus d'exécution vers fab :
 >>> for n in Fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

Listing 6. Processus d'exécution

Lorsque l'exécution de la fonction se termine, le générateur lève automatiquement une exception StopIteration, indiquant itération Terminer. Dans la boucle for, il n'est pas nécessaire de gérer l'exception StopIteration et la boucle se terminera normalement.


Nous pouvons tirer les conclusions suivantes :

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:

清单 7. 使用 isgeneratorfunction 判断

 >>> from inspect import isgeneratorfunction 
 >>> isgeneratorfunction(fab) 
 True

要注意区分 fab 和 fab(5),fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别:

清单 8. 类的定义和类的实例

 >>> import types 
 >>> isinstance(fab, types.GeneratorType) 
 False 
 >>> isinstance(fab(5), types.GeneratorType) 
 True
fab 是无法迭代的,而 fab(5) 是可迭代的:
 >>> from collections import Iterable 
 >>> isinstance(fab, Iterable) 
 False 
 >>> isinstance(fab(5), Iterable) 
 True

每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响:

>>> f1 = fab(3) 
 >>> f2 = fab(5) 
 >>> print &#39;f1:&#39;, f1.next() 
 f1: 1 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 1 
 >>> print &#39;f1:&#39;, f1.next() 
 f1: 1 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 1 
 >>> print &#39;f1:&#39;, f1.next() 
 f1: 2 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 2 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 3 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 5

return 的作用

在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

另一个例子

另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:

清单 9. 另一个 yield 的例子

 def read_file(fpath): 
  BLOCK_SIZE = 1024 
  with open(fpath, &#39;rb&#39;) as f: 
    while True: 
      block = f.read(BLOCK_SIZE) 
      if block: 
        yield block 
      else: 
        return

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