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')


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow