Sök…


Introduktion

Medan Pythons kontekthanterare används i stor utsträckning, förstår få syftet bakom deras användning. Dessa uttalanden, som vanligtvis används för att läsa och skriva filer, hjälper applikationen att spara systemminne och förbättra resurshanteringen genom att säkerställa att specifika resurser endast används för vissa processer. Detta ämne förklarar och demonstrerar användningen av Pythons kontexthanterare.

Syntax

  • med "context_manager" (som "alias") (, "context_manager" (som "alias")?) *:

Anmärkningar

Kontexthanterare definieras i PEP 343 . De är avsedda att användas som en mer kortfattad mekanism för resurshantering än try ... finally konstruerar. Den formella definitionen är som följer.

I denna PEP tillhandahåller sammanhangshanterare __enter__() och __exit__() -metoder som åberopas vid inresa till och ut ur kroppen med uttalandet.

Därefter definieras with uttalandet enligt följande.

with EXPR as VAR:
    BLOCK

Översättningen av ovanstående uttalande är:

   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)

Introduktion till kontekthanterare och med uttalande

En kontexthanterare är ett objekt som meddelas när ett kontext (ett kodblock) startar och slutar . Du använder ofta en with uttalandet. Det tar hand om anmälan.

Till exempel är filobjekt kontexthanterare. När ett sammanhang slutar stängs filobjektet automatiskt:

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

# the open_file object has automatically been closed.

Exemplet ovan är vanligtvis förenklas genom att använda as nyckelord:

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

# the open_file object has automatically been closed.

Allt som slutar exekveringen av blocket får kontexthanterarens utgångsmetod att kallas. Detta inkluderar undantag och kan vara användbart när ett fel gör att du för tidigt lämnar en öppen fil eller anslutning. Att lämna ett skript utan att stänga filer / anslutningar korrekt är en dålig idé, vilket kan orsaka dataförlust eller andra problem. Genom att använda en context manager kan du säkerställa att försiktighetsåtgärder alltid vidtas för att förhindra skada eller förlust på detta sätt. Den här funktionen lades till i Python 2.5.

Tilldela till ett mål

Många kontexthanterare returnerar ett objekt när de matas in. Du kan tilldela det objektet till ett nytt namn i with uttalandet.

Om du till exempel använder en databasanslutning i en with sats kan du få ett markörobjekt:

with database_connection as cursor:
    cursor.execute(sql_query)

Filobjekt returnerar sig själva, detta gör det möjligt att både öppna filobjektet och använda det som en sammanhangshanterare i ett uttryck:

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

Skriva din egen context manager

En context manager är alla objekt som implementerar två magiska metoder __enter__() och __exit__() (även om det också kan implementera andra metoder):

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

Om sammanhanget avslutas med ett undantag, kommer informationen om detta undantag att skickas som en trippel exc_type , exc_value , traceback (dessa är samma variabler som returneras av sys.exc_info() ). Om sammanhanget avslutas normalt är alla dessa tre argument None .

Om ett undantag inträffar och överförs till __exit__ metoden, kan metoden återgå till True för att undertrycka undantaget, eller så kommer undantaget att höjas igen i slutet av __exit__ funktionen.

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

Observera att i det andra exemplet trots att ett undantag inträffar i mitten av kroppen med med-uttalandet, __exit__ fortfarande, innan undantaget sprider sig till det yttre räckvidden.

Om du bara behöver en __exit__ metod kan du returnera instansen för context manager:

class MyContextManager:
    def __enter__(self):
        return self

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

Skriva din egen contextmanager med hjälp av generatorsyntax

Det är också möjligt att skriva en kontexthanterare med hjälp av generatorsyntax tack vare contextlib.contextmanager dekoratören:

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

producerar:

Enter
Right in the middle with cm = 3
Exit

Dekoratören förenklar uppgiften att skriva en kontexthanterare genom att konvertera en generator till en. Allt innan avkastningsuttrycket blir metoden __enter__ , det __enter__ värdet blir värdet som returneras av generatorn (som kan bindas till en variabel i med-satset), och allt efter avkastningsuttrycket blir metoden __exit__ .

Om ett undantag måste hanteras av context manager, kan ett try..except..finally -block skrivas i generatorn och alla undantag som tas upp with -block kommer att hanteras av detta undantagsblock.

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

Detta producerar:

Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit

Flera kontexthanterare

Du kan öppna flera innehållshanterare samtidigt:

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

Det har samma effekt som häckande sammanhangshanterare:

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

Hantera resurser

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__() -metoden ställer in objektet, i detta fall ställer du in filnamnet och läget för att öppna filen. __enter__() öppnar och returnerar filen och __exit__() stänger bara den.

Med hjälp av dessa magiska metoder ( __enter__ , __exit__ ) kan du implementera objekt som enkelt kan användas with med-uttalandet.

Använd File-klassen:

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow