Suche…


Einführung

Während die Kontextmanager von Python weit verbreitet sind, wissen nur wenige den Zweck ihrer Verwendung. Diese Anweisungen, die häufig beim Lesen und Schreiben von Dateien verwendet werden, unterstützen die Anwendung bei der Einsparung von Systemspeicher und verbessern die Ressourcenverwaltung, indem sie sicherstellen, dass bestimmte Ressourcen nur für bestimmte Prozesse verwendet werden. In diesem Thema wird die Verwendung der Kontextmanager von Python erläutert und veranschaulicht.

Syntax

  • mit "context_manager" (als "Alias") (, "Context_manager" (als "Alias")?) *:

Bemerkungen

Kontextmanager sind in PEP 343 definiert. Sie sind als prägnanter Mechanismus für das Ressourcenmanagement gedacht, als try ... finally Konstrukte. Die formale Definition lautet wie folgt.

In diesem PEP stellen __enter__() Methoden __enter__() und __exit__() , die beim Eintritt in den Rumpf der with-Anweisung aufgerufen werden.

Anschließend wird die with Anweisung wie folgt definiert.

with EXPR as VAR:
    BLOCK

Die Übersetzung der obigen Aussage lautet:

   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)

Einführung in Kontextmanager und die with-Anweisung

Ein Kontextmanager ist ein Objekt, das benachrichtigt wird, wenn ein Kontext (ein Codeblock) beginnt und endet . Sie verwenden häufig eine mit der with Anweisung. Es kümmert sich um die Benachrichtigung.

Beispielsweise sind Dateiobjekte Kontextmanager. Wenn ein Kontext endet, wird das Dateiobjekt automatisch geschlossen:

open_file = open(filename)
with open_file:
    file_contents = open_file.read()

# the open_file object has automatically been closed.

Das obige Beispiel wird normalerweise mit dem Schlüsselwort as vereinfacht:

with open(filename) as open_file:
    file_contents = open_file.read()

# the open_file object has automatically been closed.

Alles, was die Ausführung des Blocks beendet, führt zum Aufruf der Exit-Methode des Kontextmanagers. Dies schließt Ausnahmen ein und kann nützlich sein, wenn ein Fehler dazu führt, dass Sie eine geöffnete Datei oder Verbindung vorzeitig beenden. Das Beenden eines Skripts ohne ordnungsgemäßes Schließen von Dateien / Verbindungen ist eine schlechte Idee, die zu Datenverlust oder anderen Problemen führen kann. Durch die Verwendung eines Kontextmanagers können Sie sicherstellen, dass immer Vorkehrungen getroffen werden, um auf diese Weise Schäden oder Verluste zu vermeiden. Diese Funktion wurde in Python 2.5 hinzugefügt.

Einem Ziel zuweisen

Viele Kontextmanager geben bei der Eingabe ein Objekt zurück. Sie können dieses Objekt in der with Anweisung einem neuen Namen zuweisen.

Wenn Sie beispielsweise eine Datenbankverbindung in einer with Anweisung verwenden, erhalten Sie ein Cursorobjekt:

with database_connection as cursor:
    cursor.execute(sql_query)

Dateiobjekte geben sich selbst zurück, wodurch das Dateiobjekt geöffnet und als Kontextmanager in einem Ausdruck verwendet werden kann:

with open(filename) as open_file:
    file_contents = open_file.read()

Schreiben Sie Ihren eigenen Kontextmanager

Ein Kontextmanager ist ein Objekt, das zwei magische Methoden __enter__() und __exit__() implementiert (obwohl es auch andere Methoden implementieren kann):

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

Wenn der Kontext mit einer Ausnahme beendet wird , wird die Information über diese Ausnahme als Triple geführt werden exc_type , exc_value , traceback (das sind die gleichen Variablen wie die zurück sys.exc_info() Funktion). Wenn der Kontext normal beendet wird, sind alle drei Argumente None .

Wenn eine Ausnahme auftritt und an die __exit__ Methode übergeben wird, kann die Methode True , um die Ausnahme zu unterdrücken, oder die Ausnahme wird am Ende der __exit__ Funktion erneut __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

Beachten Sie, dass im zweiten Beispiel der __exit__ Handler weiterhin ausgeführt wird, obwohl eine Ausnahme in der Mitte des __exit__ der mit-Anweisung __exit__ , bevor die Ausnahme an den äußeren Bereich weitergegeben wird.

Wenn Sie nur eine __exit__ Methode benötigen, können Sie die Instanz des Kontextmanagers zurückgeben:

class MyContextManager:
    def __enter__(self):
        return self

    def __exit__(self):
        print('something')

Schreiben Sie Ihren eigenen Kontextmanager mit der Generatorsyntax

Dank des Decorators contextlib.contextmanager ist es auch möglich, einen Kontextmanager unter Verwendung der Generatorsyntax zu schreiben:

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

produziert:

Enter
Right in the middle with cm = 3
Exit

Der Dekorator vereinfacht das Schreiben eines Kontextmanagers durch Konvertieren eines Generators in einen. Alles vor dem Ertragsausdruck wird zur __enter__ Methode, der __enter__ wird zum vom Generator zurückgegebenen Wert (der an eine Variable in der with-Anweisung gebunden werden kann), und alles, was nach dem Ertragsausdruck geht, wird zur __exit__ Methode.

Wenn eine Ausnahmebedingung vom Kontextmanager behandelt werden muss, kann ein try..except..finally Block in den Generator geschrieben werden, und jede im with try..except..finally Ausnahme wird von diesem Ausnahmeblock behandelt.

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

Dies erzeugt:

Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit

Mehrere Kontextmanager

Sie können mehrere Content Manager gleichzeitig öffnen:

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

Dies hat den gleichen Effekt wie das Schachteln von Kontextmanagern:

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

Ressourcen verwalten

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__() richtet das Objekt ein. In diesem Fall __init__() den Dateinamen und den Modus für das Öffnen der Datei ein. __enter__() öffnet die Datei und __exit__() sie zurück. __exit__() schließt sie einfach.

Mit diesen magischen Methoden ( __enter__ , __exit__ ) können Sie Objekte implementieren, die problemlos with der with-Anweisung verwendet werden können.

Dateiklasse verwenden:

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow