Szukaj…


Wprowadzenie

Podczas gdy menedżery kontekstu Pythona są szeroko stosowane, niewielu rozumie cel ich użycia. Te instrukcje, często używane do odczytu i zapisu plików, pomagają aplikacji w oszczędzaniu pamięci systemowej i usprawniają zarządzanie zasobami, zapewniając, że określone zasoby są używane tylko dla niektórych procesów. W tym temacie wyjaśniono i pokazano użycie menedżerów kontekstu Pythona.

Składnia

  • with „context_manager” (jako „alias”) (, „context_manager” (jako „alias”)?) *:

Uwagi

Menedżery kontekstu są zdefiniowane w PEP 343 . Są przeznaczone do użycia jako bardziej zwięzły mechanizm zarządzania zasobami niż konstrukcja try ... finally konstruuje. Formalna definicja jest następująca.

W tym środowisku PEP menedżerowie kontekstu zapewniają __enter__() i __exit__() , które są wywoływane przy wejściu i wyjściu z treści instrukcji with.

Następnie przechodzi do zdefiniowania instrukcji with w następujący sposób.

with EXPR as VAR:
    BLOCK

Tłumaczenie powyższego oświadczenia jest następujące:

   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)

Wprowadzenie do menedżerów kontekstu i instrukcji with

Menedżer kontekstu to obiekt, który jest powiadamiany, gdy kontekst (blok kodu) zaczyna się i kończy . Zwykle używasz jednego z instrukcją with . Zajmuje się powiadomieniem.

Na przykład obiektami plików są menedżery kontekstu. Po zakończeniu kontekstu obiekt pliku jest automatycznie zamykany:

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

# the open_file object has automatically been closed.

Powyższy przykład zwykle upraszcza się, używając słowa kluczowego as :

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

# the open_file object has automatically been closed.

Cokolwiek, co kończy wykonywanie bloku, powoduje wywołanie metody wyjścia menedżera kontekstu. Obejmuje to wyjątki i może być przydatne, gdy błąd powoduje przedwczesne wyjście z otwartego pliku lub połączenia. Wyjście ze skryptu bez prawidłowego zamykania plików / połączeń to zły pomysł, który może spowodować utratę danych lub inne problemy. Korzystając z menedżera kontekstu, możesz upewnić się, że zawsze podejmowane są środki ostrożności, aby w ten sposób zapobiec uszkodzeniu lub utracie. Ta funkcja została dodana w Pythonie 2.5.

Przypisywanie do celu

Wielu menedżerów kontekstu zwraca obiekt po wprowadzeniu. Możesz przypisać ten obiekt do nowej nazwy w instrukcji with .

Na przykład użycie połączenia z bazą danych w instrukcji with może dać obiekt kursora:

with database_connection as cursor:
    cursor.execute(sql_query)

Obiekty plików zwracają się same, co umożliwia zarówno otwarcie obiektu pliku, jak i użycie go jako menedżera kontekstu w jednym wyrażeniu:

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

Pisanie własnego menedżera kontekstu

Menedżer kontekstu to dowolny obiekt, który implementuje dwie magiczne metody __enter__() i __exit__() (chociaż może również implementować inne metody):

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

Jeśli wyjścia kontekst z wyjątkiem, informacja o tym wyjątkiem zostanie przekazany jako potrójny exc_type , exc_value , traceback (które są takie same zmienne, jak zwracane przez sys.exc_info() funkcji). Jeśli kontekst zakończy się normalnie, wszystkie trzy argumenty będą miały wartość None .

Jeśli wystąpi wyjątek i zostanie przekazany do metody __exit__ , metoda może zwrócić wartość True w celu zlikwidowania wyjątku lub wyjątek zostanie ponownie zgłoszony na końcu funkcji __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

Zauważ, że w drugim przykładzie, mimo że wyjątek występuje w środku treści instrukcji, __exit__ obsługi __exit__ nadal jest wykonywany, zanim wyjątek przejdzie do zakresu zewnętrznego.

Jeśli potrzebujesz tylko metody __exit__ , możesz zwrócić instancję menedżera kontekstu:

class MyContextManager:
    def __enter__(self):
        return self

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

Pisanie własnego menedżera kontekstu przy użyciu składni generatora

Możliwe jest również napisanie menedżera kontekstu przy użyciu składni generatora dzięki dekoratorowi contextlib.contextmanager :

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

produkuje:

Enter
Right in the middle with cm = 3
Exit

Dekorator upraszcza zadanie napisania menedżera kontekstu, przekształcając generator w jeden. Wszystko przed wyrażeniem wydajności staje się metodą __enter__ , __enter__ wartość staje się wartością zwracaną przez generator (który może być powiązany ze zmienną w instrukcji with), a wszystko po wyrażeniu wydajności staje się metodą __exit__ .

Jeśli potrzeby an exception być obsługiwane przez kierownika kontekście try..except..finally -blok mogą być napisane w generatorze i każdy wyjątek podniesiony w with -blok będą obsługiwane przez tego bloku wyjątków.

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

Daje to:

Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit

Wielu menedżerów kontekstu

Możesz otworzyć jednocześnie kilka menedżerów treści:

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

Ma taki sam efekt jak zagnieżdżanie menedżerów kontekstu:

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

Zarządzaj zasobami

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__() ustawia obiekt, w tym przypadku ustawiając nazwę pliku i tryb otwierania pliku. __enter__() otwiera i zwraca plik, a __exit__() tylko go zamyka.

Korzystanie z tych magicznych metod ( __enter__ , __exit__ ) pozwala na implementację obiektów, których można łatwo używać with instrukcją with.

Użyj klasy pliku:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow