Как не думать о последствиях при приобретении ресурсов

Представьте себе: вы автоматизировали какие-то действия на веб-странице через Selenium, и все отлично работает. Но бывают случаи, когда он может дать сбой. Вы не хотите if каждый шаг, который может пойти не так.

Очень изящным решением будет контекстный менеджер. Вы уже использовали его при открытии файлов. Этот with open является диспетчером контекста. Он пытается получить ресурс и закрывает его, если что-то пойдет не так, освобождая данный ресурс.

Вернемся к нашему примеру с Selenium. Когда происходит сбой, окно браузера не закрывается, оставляя его висеть до тех пор, пока вы его не закроете. Было бы здорово, если бы это окно закрывалось в случае сбоя программы?

Есть несколько способов реализовать Context Manager в Python. Во-первых, это определение класса:

from settings import web_driver
class ContextDriver:
    def __init__(self):
        self.driver = web_driver
    def __enter__(self):
        return self.driver
    def __exit__(self):
        self.driver.close()

Драйвер в настройках представляет собой простой веб-драйвер Firefox selenium:

from selenium import webdriver
web_driver = webdriver.Firefox()

И это все! Теперь вы можете написать with ContextDriver() as driver и использовать это driver для выполнения автоматизации. Поэтому, если что-то пойдет не так, окно браузера закроется.

На мой взгляд, писать класс для такой простой задачи избыточно. К счастью, в Python у нас есть полезный декоратор:

from contextlib import contextmanager
from settings import web_driver
@contextmanager
def ContextDriver():
    try:
        yield web_driver
    finally:
        web_driver.close()

Аккуратно, не так ли? На самом деле, я впервые написал этот Context Manager через декоратор, когда столкнулся с этой проблемой на работе. И потратил некоторое время на то, чтобы переписать его как класс для этой статьи. Это не означает, что вы должны знать только, как это сделать с помощью декоратора, хотя это намного проще. Бывают случаи, когда вам понадобится класс, и в одной из следующих статей я покажу это.

И есть еще один способ определить контекстный менеджер. Просто импортируйте закрытие и используйте with closing(driver) as…Это работает со всем, что имеет метод close(). Но я не большой любитель этого.

Итак, теперь вы знаете, зачем и как использовать контекстный менеджер! Но просто знать недостаточно. Попробуйте это в своем следующем проекте. Или даже в вашем нынешнем! Таким образом, вы будете уверены, что можете реализовать это везде, где это подходит. Кроме того, это одна из тем, о которых любят спрашивать технические интервьюеры.

Вы также можете найти эту и другие статьи в моем блоге на github.