Cześć.
Menadżery kontekstu w Pythonie to dość użyteczne narzędzie, którego głównym zadaniem jest…. Chronić programistów przed ich zapominalstwem. Już na samym początku nauki programowania spotykamy się enigmatyczną instrukcją with, która w połączeniu z funkcją open pozwala na pracę z plikami. Dziś przyjrzymy się owej instrukcji trochę głębiej. Zapraszam.
Odpowiedź jest prosta – żeby nie musieć pamiętać o tym, że zasoby których używamy, należy zwalniać. Takim najbardziej powszechnie używanym zasobem jest pamięć. I jak pewnie wiesz, alokujemy ją tworząc zmienne, ale… Nigdy nie zwalniamy! Pamięć jest tak powszechnie używanym zasobem, że Python, podobnie jak inne języki, sam zarządza zwalnianiem pamięci dzięki algorytmowi zliczania referencji i aplikacji zwanej garbage collector.
Pamięć nie jest jedynym zasobem którego możemy używać. Pisząc o menadżerach kontekstu nie da się nie wspomnieć o powszechnie używanej funkcji open – czyli bezsprzecznie najpopularniejszym wbudowanym obiektem tego typu.
Funkcję open możemy używać na dwa podstawowe sposoby. Pierwszy, tradycyjny sposób to:
1 2 3 4 | file = open("text.txt") lines = file.readlines() print(lines) file.close() |
a drugi, używający menadżera to:
1 2 3 | with open("text.txt") as file: lines = file.readlines() print(lines) |
To co warto tu zauważyć, to fakt, że w przypadku tradycyjnego użycia, musimy pamiętać o wywołaniu metody close(). Natomiast w przypadku menadżera jesteśmy z tego zwolnieni.
Wygoda rośnie wraz z ilością plików na których pracujemy, ponieważ nic nie stoi na przeszkodzie, żeby otworzyć dwa pliki i np. przepisać zawartość jednego do drugiego. Wciąż nie musząc pamiętać o zamykaniu otwartych plików:
1 2 | with open("text.txt") as file, open("text2.txt", "w") as file2: file2.write(file.read()) |
Żeby dobrze zrozumieć jak działa menadżer kontekstu, napiszmy własny obiekt OwnOpen który będzie dostarczał podobna funkcjonalność co oryginalna funkcja open:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class OwnOpen: def __init__(self, filename, mode="r"): self._filename = filename self._mode = mode self._file_handler = None def __enter__(self): self._file_handler = open(self._filename, self._mode) return self def __exit__(self, exc_type, exc_val, exc_tb): self._file_handler.close() def own_read(self): return self._file_handler.read() |
Najważniejsze w tym snippecie są definicje metod __enter__ i __exit__.
Pierwsza ze wspomnianych metod, czyli __enter__ wykonuje się przed kodem znajdującym się pod zakresem słówka with a druga – po wykonaniu się tego kodu. Pierwsza powinna zwracać obiekt na którym będziemy pracować, który potem będzie dostępny w zmiennej wskazanej po słówku as, druga zaś nie musi nic zwracać.
Jeśli w obrębie kodu znajdującego się pod słówkiem with zostanie rzucony wyjątek, dowiemy się o tym analizując argumenty funkcji __exit__ – pozwala to na jego ewentualne obsłużenie wyjątku. Jeśli wyjątek ma być ukryty, funkcja __exit__ powinna zwracać True.
Popatrz na poniższy kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class OwnOpen: def __init__(self, filename, mode="r"): self._filename = filename self._mode = mode self._file_handler = None def __enter__(self): self._file_handler = open(self._filename, self._mode) return self def __exit__(self, exc_type, exc_val, exc_tb): self._file_handler.close() if isinstance(exc_val, TypeError): return return True def own_read(self): return self._file_handler.read() with OwnOpen("text.txt") as file, open("text2.txt", "w") as file2: # raise Exception() or TypeError() file2.write(file.own_read()) |
Jeśli przy tak zdefiniowanej funkcji __exit_, pod słówkiem with pojawi się wyjątek TypeError to zostanie on rzucony (ponieważ __exit__ zwróci None), natomiast każdy inny wyjątek zostanie ukryty.
Zabawa z __enter__ i __exit__ jest fajna, ale generuje trochę za dużo kodu. Python pozwala zrobić to krócej, korzystając ze specjalnego dekoratora:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from contextlib import contextmanager @contextmanager def own_open(filename, mode="r"): file_handler = open(filename, mode) try: yield file_handler finally: file_handler.close() with own_open("text.txt") as file, open("text2.txt", "w") as file2: file2.write(file.read()) |
Ten sprytny skrót wymaga od nas stworzenia funkcji generującej, która będzie „podzielona” słówkiem yield na kawałki kodu odpowiadającej odpowiednio:
Fajne, prawda?
Fajnie, że sobie powiedzieliśmy czym są menadżery kontekstu ale nadal mamy tylko ten jeden przykład. Trochę mało. Ale spokojnie, menadżery kontekstu są dość często używane, spotkamy je na przykład w takich zastosowaniach:
Czasem menadżer kontekstu przydaje się w sytuacjach gdzie chcemy w jakiś sposób zbadać kawałek kodu. Np. zmierzyć czas wykonywania:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from contextlib import contextmanager from time import time, sleep @contextmanager def time_it(): start = time() try: yield finally: stop = time() print(f"Duration: {stop-start}") with time_it(): sleep(1) |
I to by było na tyle. Menadżery kontekstu to potężne narzędzie, którego głównym zadaniem jest odciążenie programistów z konieczności pamiętania o zwalnianiu zasobów. Mam nadzieję, że artykuł okazał się ciekawy i że dzięki niemu zaczniesz świadomej używać składni with .. as a może i gdzieniegdzie tworzyć własne menadżery.
Oj daaawnoo mnie tu nie było. Ale wakacje to był czas dużej liczby intensywnych wyjazdów i tak naprawdę, dopiero jakoś… Read More
Cześć! Zapraszam na krótkie podsumowanie kwietnia. Wyjazd do Niemiec A dokładniej pod granicę z Francją. Chrześnica miała pierwszą komunię. Po… Read More
Ostatnio tygodnie były tak bardzo wypełnione, że nie udało mi się napisać nawet krótkiego podsumowanie. Więc dziś zbiorczo podsumuję luty… Read More
Zapraszam na krótkie podsumowanie miesiąca. Książki W styczniu przeczytałem "Homo Deus: Historia jutra". Książka łudząco podoba do wcześniejszej książki tego… Read More
Cześć! Zapraszam na podsumowanie roku 2023. Książki Zacznijmy od książek. W tym roku cel 35 książek nie został osiągnięty. Niemniej… Read More
Zapraszam na krótkie podsumowanie miesiąca. Książki W grudniu skończyłem czytać Mein Kampf. Nudna książka. Ciekawsze fragmenty można by było streścić… Read More
Pokaż komentarze
Świetne treści, ale layout bloga jest okropny.
Ooooo z taką opinią się nie spotkałem jeszcze! :D napiszesz coś więcej?
Zbyt duża ilość sekcji trochę przytłacza. Brak możliwości przełączenia na dark mode.
Darkmode - mogę coś pokminić, a sekcje rozumiem że masz na myśli te po prawej?
Fajny artykuł. Trochę mi przybliżył po co w ogóle jest Menadżer Kontekstu.
Mi się layout strony podoba jednak i tak głównie patrzę na treść która jest super.
nie zgodzę się z poprzednikiem layout jest w porządku, czysty, przejrzysty. właśnie fajne jest to ze jest biały. ja nie lubię dark
Dziękuję!