Python Language
Contextmanagers ('met' verklaring)
Zoeken…
Invoering
Hoewel contextbeheerders van Python veel worden gebruikt, begrijpen maar weinigen het doel achter hun gebruik. Deze verklaringen, vaak gebruikt bij het lezen en schrijven van bestanden, helpen de toepassing bij het bewaren van systeemgeheugen en verbeteren het resourcebeheer door ervoor te zorgen dat specifieke resources alleen voor bepaalde processen worden gebruikt. In dit onderwerp wordt het gebruik van contextmanagers van Python uitgelegd en gedemonstreerd.
Syntaxis
- met "context_manager" (als "alias") (, "context_manager" (als "alias")?) *:
Opmerkingen
Contextmanagers worden gedefinieerd in PEP 343 . Ze zijn bedoeld om te worden gebruikt als een meer beknopt mechanisme voor resource management dan try ... finally
constructies. De formele definitie is als volgt.
In deze PEP bieden
__enter__()
en__exit__()
methoden die worden aangeroepen bij binnenkomst en uitgang uit de body van de with-instructie.
Vervolgens wordt de instructie with
als volgt gedefinieerd.
with EXPR as VAR: BLOCK
De vertaling van bovenstaande verklaring is:
mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # Only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns true finally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
Inleiding tot contextmanagers en de met-verklaring
Een contextmanager is een object dat wordt gewaarschuwd wanneer een context (een codeblok) begint en eindigt . U gebruikt er meestal een met de instructie with
. Het zorgt voor de melding.
Bestandsobjecten zijn bijvoorbeeld contextbeheerders. Wanneer een context eindigt, wordt het bestandsobject automatisch gesloten:
open_file = open(filename)
with open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
Het bovenstaande voorbeeld wordt meestal vereenvoudigd door het trefwoord as
:
with open(filename) as open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
Alles wat de uitvoering van het blok beëindigt, zorgt ervoor dat de exit-methode van de contextmanager wordt aangeroepen. Dit omvat uitzonderingen en kan handig zijn wanneer een fout ertoe leidt dat u een open bestand of verbinding voortijdig verlaat. Een script afsluiten zonder bestanden / verbindingen correct te sluiten is een slecht idee, dat gegevensverlies of andere problemen kan veroorzaken. Door een contextmanager te gebruiken, kunt u ervoor zorgen dat er altijd voorzorgsmaatregelen worden genomen om schade of verlies op deze manier te voorkomen. Deze functie is toegevoegd in Python 2.5.
Toewijzen aan een doelwit
Veel contextmanagers retourneren een object wanneer ze worden ingevoerd. U kunt dat object aan een nieuwe naam toewijzen in de instructie with
.
Als u bijvoorbeeld een databaseverbinding in een instructie with
, krijgt u een cursorobject:
with database_connection as cursor:
cursor.execute(sql_query)
Bestandsobjecten retourneren zichzelf, dit maakt het mogelijk om zowel het bestandsobject te openen als het als contextmanager in één expressie te gebruiken:
with open(filename) as open_file:
file_contents = open_file.read()
Uw eigen contextmanager schrijven
Een contextmanager is een object dat twee magische methoden __enter__()
en __exit__()
(hoewel het ook andere methoden kan implementeren):
class AContextManager():
def __enter__(self):
print("Entered")
# optionally return an object
return "A-instance"
def __exit__(self, exc_type, exc_value, traceback):
print("Exited" + (" (with an exception)" if exc_type else ""))
# return True if you want to suppress the exception
Als de context met een uitzondering wordt afgesloten, wordt de informatie over die uitzondering doorgegeven als een drievoudig exc_type
, exc_value
, traceback
(dit zijn dezelfde variabelen die worden geretourneerd door de functie sys.exc_info()
). Als de context normaal wordt afgesloten, zijn alle drie deze argumenten None
.
Als er een uitzondering optreedt en wordt doorgegeven aan de methode __exit__
, kan de methode True
retourneren om de uitzondering te onderdrukken, of wordt de uitzondering opnieuw verhoogd aan het einde van de functie __exit__
.
with AContextManager() as a:
print("a is %r" % a)
# Entered
# a is 'A-instance'
# Exited
with AContextManager() as a:
print("a is %d" % a)
# Entered
# Exited (with an exception)
# Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
# TypeError: %d format: a number is required, not str
Merk op dat in het tweede voorbeeld, hoewel er een uitzondering optreedt in het midden van de with-statement, de __exit__
handler nog steeds wordt uitgevoerd, voordat de uitzondering zich naar het buitenbereik verspreidt.
Als u alleen een __exit__
methode nodig hebt, kunt u het exemplaar van contextbeheer retourneren:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self):
print('something')
Uw eigen contextmanager schrijven met behulp van generatorsyntaxis
Het is ook mogelijk om een contextmanager te schrijven met behulp van generatorsyntaxis dankzij de contextlib.contextmanager
decorator:
import contextlib
@contextlib.contextmanager
def context_manager(num):
print('Enter')
yield num + 1
print('Exit')
with context_manager(2) as cm:
# the following instructions are run when the 'yield' point of the context
# manager is reached.
# 'cm' will have the value that was yielded
print('Right in the middle with cm = {}'.format(cm))
produceert:
Enter
Right in the middle with cm = 3
Exit
De decorateur vereenvoudigt de taak van het schrijven van een contextmanager door een generator in één te converteren. Alles voordat de opbrengstuitdrukking de __enter__
methode wordt, wordt de __enter__
waarde de waarde die door de generator wordt geretourneerd (die aan een variabele in de instructie with kan worden gebonden) en alles na de opbrengstuitdrukking wordt de __exit__
methode.
Als een uitzondering door de contextmanager moet worden afgehandeld, kan een try..except..finally
-blok in de generator worden geschreven en elke uitzondering die in het with
-blok wordt opgeworpen, wordt door dit uitzonderingsblok afgehandeld.
@contextlib.contextmanager
def error_handling_context_manager(num):
print("Enter")
try:
yield num + 1
except ZeroDivisionError:
print("Caught error")
finally:
print("Cleaning up")
print("Exit")
with error_handling_context_manager(-1) as cm:
print("Dividing by cm = {}".format(cm))
print(2 / cm)
Dit levert:
Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit
Meerdere contextmanagers
U kunt meerdere inhoudbeheerders tegelijkertijd openen:
with open(input_path) as input_file, open(output_path, 'w') as output_file:
# do something with both files.
# e.g. copy the contents of input_file into output_file
for line in input_file:
output_file.write(line + '\n')
Het heeft hetzelfde effect als nesting context managers:
with open(input_path) as input_file:
with open(output_path, 'w') as output_file:
for line in input_file:
output_file.write(line + '\n')
Beheer bronnen
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.open_file = open(self.filename, self.mode)
return self.open_file
def __exit__(self, *args):
self.open_file.close()
__init__()
methode __init__()
stelt het object in, in dit geval de bestandsnaam en de modus om het bestand te openen. __enter__()
opent en retourneert het bestand en __exit__()
sluit het gewoon.
Met deze magische methoden ( __enter__
, __exit__
) kunt u objecten implementeren die eenvoudig kunnen worden gebruikt with
de instructie with.
Gebruik bestandsklasse:
for _ in range(10000):
with File('foo.txt', 'w') as f:
f.write('foo')