Python Language
Menedżerowie kontekstu (oświadczenie „z”)
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')