Maison  >  Article  >  développement back-end  >  Explication détaillée de l'utilisation du mot-clé with en Python

Explication détaillée de l'utilisation du mot-clé with en Python

高洛峰
高洛峰original
2017-03-28 15:21:121540parcourir

Cet article présente principalement des informations pertinentes sur l'utilisation détaillée du mot-clé with en Python En Python, le mot-clé with est une bonne chose pour gérer l'implémentation des objets de protocole contextuel pour vous. it Vous pouvez vous référer à ce qui suit

">

Dans Python 2.5, le mot-clé with a été ajouté. Il rend le modèle try ... except ... enfin ... couramment utilisé Réutilisation très pratique. Regardez l'exemple le plus classique :

with open('file.txt') as f:
  content = f.read()

Dans ce code, peu importe ce qui se passe lors de l'exécution du bloc de code dans with, le fichier sera finalement fermé si une exception se produit pendant. exécution, le programme fermera d'abord le fichier ouvert avant que l'exception ne soit levée.

Regardez un autre exemple avant de lancer une base de données. Lors des demandes de transaction, un code similaire à celui-ci est souvent utilisé :

.

Si l'opération de lancement d'une demande de transaction est modifiée pour prendre en charge le mot-clé with, alors un code comme celui-ci suffit :
db.begin()
try:
  # do some actions
except:
  db.rollback()
  raise
finally:
  db.commit()

Le processus d'exécution de with est expliqué en détail, et le code ci-dessus est implémenté de deux manières courantes.
with transaction(db):
  # do some actions

Le processus général d'exécution de with

Une base avec

expression

, sa structure est la suivante : où : EXPR peut être n'importe quelle expression ; car VAR est facultatif pour son exécution générale. Le processus est le suivant :

with EXPR as VAR:
  BLOCK

Calculez EXPR et obtenez un gestionnaire de contexte.
  1. La méthode exit() du gestionnaire de contexte est enregistrée pour les appels ultérieurs
  2. Appelez la méthode enter() du gestionnaire de contexte. .
  3. Si l'expression with contient comme VAR, alors EXPR La valeur de retour est affectée à VAR
  4. Exécutez l'expression dans BLOCK if. . Si une exception se produit pendant l'exécution de BLOCK et provoque la fermeture du programme, le type d'exception, la valeur et le traçage (c'est-à-dire la valeur de retour de sys.exc_info()) seront transmis en tant que paramètres à la méthode exit(). , trois Aucun ne sera réussi
  5. Représentez ce processus en code, comme suit :
  6. Ce processus a plusieurs détails :

  7. Si dans le gestionnaire de contexte Sans aucune des méthodes enter() ou exit(), l'interpréteur lancera une AttributeError.
Après qu'une exception se soit produite dans BLOCK, si la méthode exit() renvoie une valeur qui peut être considérée comme True, alors l'exception ne sera pas levée et le code suivant continuera à s'exécuter.

Ensuite, utilisons deux méthodes pour mettre en œuvre le processus ci-dessus.
mgr = (EXPR)
exit = type(mgr).exit # 这里没有执行
value = type(mgr).enter(mgr)
exc = True
try:
  try:
    VAR = value # 如果有 as VAR
    BLOCK
  except:
    exc = False
    if not exit(mgr, *sys.exc_info()):
      raise
finally:
  if exc:
    exit(mgr, None, None, None)

Implémentez la classe du gestionnaire de contexte

La première façon consiste à implémenter une classe qui contient une instance
attributs

db et les méthodes requises par le gestionnaire de contexte enter() et exit () .

Après avoir compris le processus d'exécution de with, cette implémentation est facile à comprendre. La méthode de mise en œuvre présentée ci-dessous est beaucoup plus compliquée à comprendre.

Utilisation de Générateurs

Décorateurs
class transaction(object):
  def init(self, db):
    self.db = db
  def enter(self):
    self.db.begin()
  def exit(self, type, value, traceback):
    if type is None:
      db.commit()
    else:
      db.rollback()

Dans la bibliothèque standard de Python, il existe un décorateur qui peut obtenir un gestionnaire de contexte via un générateur. Le processus d'implémentation à l'aide du générateur décorateur est le suivant :

À première vue, cette implémentation est plus simple, mais son mécanisme est plus complexe. Jetons un coup d'œil à son processus d'exécution :

Une fois que l'interpréteur Python a reconnu le mot-clé rendement, def créera une

fonction génératrice
from contextlib import contextmanager
@contextmanager
def transaction(db):
  db.begin()
  try:
    yield db
  except:
    db.rollback()
    raise
  else:
    db.commit()
pour remplacer la fonction régulière (dans le définition de classe (j'aime utiliser des fonctions plutôt que des méthodes).

  1. Le contextmanager du décorateur est appelé et renvoie une méthode d'assistance, qui générera une instance de GeneratorContextManager après avoir été appelée. Enfin, l'EXPR dans l'expression with appelle la fonction d'assistance renvoyée par le décorateur contentmanager. L'expression

    with appelle transaction(db), qui appelle en fait la fonction d'assistance. La fonction d'assistance appelle la fonction générateur, qui crée un générateur.
  2. La fonction d'assistance transmet ce générateur à GeneratorContextManager et crée un objet instance de GeneratorContextManager en tant que gestionnaire de contexte.
  3. with 表达式调用实例对象的上下文管理器的 enter() 方法。

  4. enter() 方法中会调用这个生成器的 next() 方法。这时候,生成器方法会执行到 yield db 处停止,并将 db 作为 next() 的返回值。如果有 as VAR ,那么它将会被赋值给 VAR 。

  5. with 中的 BLOCK 被执行。

  6. BLOCK 执行结束后,调用上下文管理器的 exit() 方法。 exit() 方法会再次调用生成器的 next() 方法。如果发生 StopIteration 异常,则 pass 。

  7. 如果没有发生异常生成器方法将会执行 db.commit() ,否则会执行 db.rollback() 。

再次看看上述过程的代码大致实现:

def contextmanager(func):
  def helper(*args, **kwargs):
    return GeneratorContextManager(func(*args, **kwargs))
  return helper
class GeneratorContextManager(object):
  def init(self, gen):
    self.gen = gen
  def enter(self):
    try:
      return self.gen.next()
    except StopIteration:
      raise RuntimeError("generator didn't yield")
  def exit(self, type, value, traceback):
    if type is None:
      try:
        self.gen.next()
      except StopIteration:
        pass
      else:
        raise RuntimeError("generator didn't stop")
    else:
      try:
        self.gen.throw(type, value, traceback)
        raise RuntimeError("generator didn't stop after throw()")
      except StopIteration:
        return True
      except:
        if sys.exc_info()[1] is not value:
          raise

总结

Python的 with 表达式包含了很多Python特性。花点时间吃透 with 是一件非常值得的事情。

一些其他的例子

锁机制

@contextmanager
def locked(lock):
  lock.acquired()
  try:
    yield
  finally:
    lock.release()

标准输出重定向

@contextmanager
def stdout_redirect(new_stdout):
  old_stdout = sys.stdout
  sys.stdout = new_stdout
  try:
    yield
  finally:
    sys.stdout = old_stdout
with open("file.txt", "w") as f:
  with stdout_redirect(f):
    print "hello world"

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