Wofür wurde die Python-Anweisung "with" entwickelt?


417

Ich bin withheute zum ersten Mal auf die Python- Anweisung gestoßen. Ich benutze Python seit einigen Monaten leicht und wusste nicht einmal von seiner Existenz! Angesichts seines etwas dunklen Status dachte ich, es lohnt sich zu fragen:

  1. Wofür soll die Python- withAnweisung verwendet werden?
  2. Wofür verwendest du es?
  3. Gibt es Fallstricke, die ich beachten muss, oder allgemeine Anti-Muster, die mit ihrer Verwendung verbunden sind? Gibt es Fälle, in denen es besser ist try..finallyals with?
  4. Warum wird es nicht weiter verbreitet?
  5. Welche Standardbibliotheksklassen sind damit kompatibel?

5
Nur zur Veranschaulichung, hier istwith in der Python 3-Dokumentation.
Alexey

Aus einem Java-Hintergrund kommend, hilft es mir, mich daran als den entsprechenden "Versuch mit Ressourcen" in Java zu erinnern , auch wenn dies möglicherweise nicht ganz richtig ist.
vefthym

Antworten:


399
  1. Ich glaube, dies wurde bereits von anderen Benutzern vor mir beantwortet, daher füge ich es der Vollständigkeit halber hinzu: Die withAnweisung vereinfacht die Ausnahmebehandlung, indem sie allgemeine Vorbereitungs- und Bereinigungsaufgaben in sogenannten Kontextmanagern kapselt . Weitere Details finden Sie in PEP 343 . Beispielsweise ist die openAnweisung ein eigenständiger Kontextmanager, mit dem Sie eine Datei öffnen, geöffnet lassen können, solange sich die Ausführung im Kontext der withAnweisung befindet, in der Sie sie verwendet haben, und sie schließen können, sobald Sie den Kontext verlassen. egal ob Sie es aufgrund einer Ausnahme oder während des regulären Kontrollflusses verlassen haben. Die withAnweisung kann daher auf ähnliche Weise wie das RAII-Muster in C ++ verwendet werden: Einige Ressourcen werden von derwithAnweisung und freigegeben, wenn Sie den withKontext verlassen .

  2. Einige Beispiele sind: Öffnen von Dateien mit with open(filename) as fp:, Erfassen von Sperren mit with lock:(wo lockist eine Instanz von threading.Lock). Sie können auch Ihre eigenen Kontextmanager mit dem contextmanagerDekorator von erstellen contextlib. Zum Beispiel benutze ich dies oft, wenn ich das aktuelle Verzeichnis vorübergehend ändern und dann dorthin zurückkehren muss, wo ich war:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    Hier ist ein weiteres Beispiel , das vorübergehend umleitet sys.stdin, sys.stdoutund sys.stderrzu einem anderen Datei - Handle und stellt sie wieder her später:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    Und schließlich ein weiteres Beispiel, das einen temporären Ordner erstellt und beim Verlassen des Kontexts bereinigt:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want

20
Vielen Dank, dass Sie den Vergleich zu RAII hinzugefügt haben. Als C ++ - Programmierer sagte mir das alles, was ich wissen musste.
Fred Thomsen

Okay, lassen Sie mich das klarstellen. Sie sagen, dass die withAnweisung eine Variable mit Daten füllen soll, bis die Anweisungen darunter vollständig sind, und dann die Variable freigeben?
Musixauce3000

Weil ich damit ein Py-Skript geöffnet habe. with open('myScript.py', 'r') as f: pass. Ich hatte erwartet, die Variable aufrufen zu können, um den fTextinhalt des Dokuments anzuzeigen, da dies der Fall wäre, wenn das Dokument füber eine reguläre openAnweisung zugewiesen würde : f = open('myScript.py').read(). Aber stattdessen habe ich folgendes bekommen : <_io.TextIOWrapper name='myScript.py' mode='r' encoding='cp1252'>. Was bedeutet das?
Musixauce3000

3
@ Musixauce3000 - Mit withwird die Notwendigkeit für readdie eigentliche Datei nicht beseitigt . Bei den withAnrufen open- es weiß nicht, was Sie damit tun müssen - möchten Sie beispielsweise eine Suche durchführen.
Tony Suffolk 66

@ Musixauce3000 Die withAnweisung kann eine Variable mit Daten füllen oder andere Änderungen an der Umgebung vornehmen, bis die Anweisungen darunter vollständig sind, und dann jede Art von Bereinigung durchführen, die erforderlich ist. Die Arten der Bereinigung, die durchgeführt werden können, sind beispielsweise das Schließen einer geöffneten Datei oder wie bei @Tamas in diesem Beispiel das Ändern der Verzeichnisse auf den vorherigen Stand usw. Da Python über eine Speicherbereinigung verfügt, ist das Freigeben einer Variablen nicht wichtig Anwendungsfall. withwird im Allgemeinen für andere Arten der Bereinigung verwendet.
Bob Steinke

89

Ich würde zwei interessante Vorträge vorschlagen:

1. Die withAnweisung wird verwendet, um die Ausführung eines Blocks mit Methoden zu versehen, die von einem Kontextmanager definiert wurden. Auf diese Weise können allgemeine try...except...finallyVerwendungsmuster für eine bequeme Wiederverwendung gekapselt werden.

2. Sie könnten so etwas tun wie:

with open("foo.txt") as foo_file:
    data = foo_file.read()

ODER

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

ODER (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

ODER

lock = threading.Lock()
with lock:
    # Critical section of code

3. Ich sehe hier kein Antipattern. Dive in Python
zitieren :

versuchen .. endlich ist gut. mit ist besser.

4. Ich denke, es hängt mit der Gewohnheit der Programmierer zusammen, try..catch..finallyAussagen aus anderen Sprachen zu verwenden.


4
Es kommt wirklich zur Geltung, wenn Sie mit Threading-Synchronisationsobjekten arbeiten. Relativ selten in Python, aber wenn Sie sie brauchen, brauchen Sie sie wirklich with.
Detly

1
diveintopython.org ist ausgefallen (dauerhaft?). Gespiegelt bei diveintopython.net
kuschelt sich

Beispiel für eine gute Antwort, offene Datei ist ein Paradebeispiel, das hinter den Kulissen des Öffnens, io und Schließens der Dateioperationen zeigt, dass sie sauber mit einem benutzerdefinierten Referenznamen verborgen sind
Angry 84,

40

Die Python- withAnweisung ist eine integrierte Sprachunterstützung für die Resource Acquisition Is Initializationin C ++ häufig verwendete Sprache . Es soll die sichere Erfassung und Freigabe von Betriebssystemressourcen ermöglichen.

Die withAnweisung erstellt Ressourcen innerhalb eines Bereichs / Blocks. Sie schreiben Ihren Code mit den Ressourcen innerhalb des Blocks. Wenn der Block beendet wird, werden die Ressourcen unabhängig vom Ergebnis des Codes im Block sauber freigegeben (dh ob der Block normal beendet wird oder aufgrund einer Ausnahme).

Viele Ressourcen in der Python-Bibliothek, die dem von der withAnweisung geforderten Protokoll entsprechen und daher sofort verwendet werden können. Jeder kann jedoch Ressourcen erstellen, die in einer with-Anweisung verwendet werden können, indem das gut dokumentierte Protokoll implementiert wird: PEP 0343

Verwenden Sie es immer dann, wenn Sie Ressourcen in Ihrer Anwendung erwerben, auf die explizit verzichtet werden muss, z. B. Dateien, Netzwerkverbindungen, Sperren und dergleichen.


27

Der Vollständigkeit halber werde ich noch einmal meinen nützlichsten Anwendungsfall für withAussagen hinzufügen .

Ich mache viel wissenschaftliches Rechnen und für einige Aktivitäten brauche ich die DecimalBibliothek für willkürliche Präzisionsberechnungen. Ein Teil meines Codes benötigt hohe Präzision und für die meisten anderen Teile weniger Präzision.

Ich setze meine Standardgenauigkeit auf eine niedrige Zahl und verwende dann with, um für einige Abschnitte eine genauere Antwort zu erhalten:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

Ich verwende dies häufig beim hypergeometrischen Test, bei dem große Zahlen in Form von Fakultäten unterteilt werden müssen. Wenn Sie genomische Skalenberechnungen durchführen, müssen Sie auf Rundungs- und Überlauffehler achten.


26

Ein Beispiel für ein Antimuster könnte darin bestehen, das withInnere einer Schleife zu verwenden, wenn es effizienter wäre, das withÄußere der Schleife zu haben

zum Beispiel

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

vs.

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

Der erste Weg ist das Öffnen und Schließen der Datei für jeden, rowwas zu Leistungsproblemen führen kann, im Vergleich zum zweiten Weg, bei dem die Datei nur einmal geöffnet und geschlossen wird.


10

Siehe PEP 343 - Die 'with'-Anweisung , am Ende befindet sich ein Beispielabschnitt.

... neue Anweisung "with" in der Python-Sprache, um die Standardverwendung von try / finally-Anweisungen herauszufiltern.


5

Die Punkte 1, 2 und 3 sind hinreichend gut abgedeckt:

4: Es ist relativ neu und nur in Python2.6 + (oder Python2.5 mit from __future__ import with_statement) verfügbar.



3

Ein weiteres Beispiel für die sofort einsatzbereite Unterstützung, das zunächst etwas verwirrend sein kann, wenn Sie an das Verhalten von integrierten Geräten gewöhnt sind open(), sind connectionObjekte beliebter Datenbankmodule wie:

Die connectionObjekte sind Kontextmanager und können als solche sofort in einem verwendet werden. with-statementBeachten Sie jedoch Folgendes:

Wenn das with-blockbeendet ist, entweder mit einer Ausnahme oder ohne, wird die Verbindung nicht geschlossen . Im Fall , dass die with-blockOberflächen mit einer Ausnahme, wird die Transaktion zurückgerollt, andernfalls wird die Transaktion begangen wird.

Dies bedeutet, dass der Programmierer darauf achten muss, die Verbindung selbst zu schließen, aber es ermöglicht, eine Verbindung herzustellen und in mehreren zu verwenden with-statements, wie in den psycopg2-Dokumenten gezeigt :

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

Im obigen Beispiel werden Sie feststellen, dass die cursorObjekte von psycopg2auch Kontextmanager sind. Aus der entsprechenden Dokumentation zum Verhalten:

Wenn eine cursorder verlässt with-blocksie geschlossen ist , jede Ressource Freigabe schließlich mit ihm verbunden ist . Der Status der Transaktion ist nicht betroffen.


3

In Python wird die Anweisung " with " im Allgemeinen verwendet, um eine Datei zu öffnen, die in der Datei vorhandenen Daten zu verarbeiten und die Datei zu schließen, ohne eine close () -Methode aufzurufen. Die Anweisung "with" vereinfacht die Ausnahmebehandlung, indem Bereinigungsaktivitäten bereitgestellt werden.

Allgemeine Form von mit:

with open(“file name”, mode”) as file-var:
    processing statements

Hinweis: Sie müssen die Datei nicht schließen, indem Sie close () bei file-var.close () aufrufen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.