Когда вам следует их использовать

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

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

И этот менеджер называется менеджером контекста. Это именно то, что делают для вас менеджеры контекста: они создают вам объект, а затем избавляются от него, когда он больше не нужен, или они устанавливают соединение с базой данных, а затем отключаются, когда это необходимо, они открывают файл для вас, а затем закрывают это в конце концов.

Они делают даже больше. Если гость в вашем отеле решил выпрыгнуть из окна, значит, гость не вышел из комнаты, диспетчер контекста все равно выключит свет. То есть, если вы откроете файл, а затем возникнет исключение, файл все равно будет закрыт.

Посмотрим на код. У нас есть класс для гостиничного номера, и мы хотим быть уверены, что свет не горит, если никого нет.

class ObjectNotFoundError(ValueError):
    ...

class Room:
    def __init__(self, guest_name):
        self.guest_name = guest_name
        self.lights = False

    def enter_room(self):
        self.lights = True

    def leave_room(self):
        self.lights = False

    def jump_out_of_window(self):
        print("See you next time")
        raise ObjectNotFoundError

Итак, у нас есть базовые методы, такие как enter_room, leave_room и, конечно же, jump_out_of_window.

Мы можем создать объект как обычно, но в отеле есть прекрасный менеджер для обработки всех гостей:

class RoomManager(object):
    def __init__(self, name):
        self.room = Room(name)
        print("The room is ready")

    def __enter__(self):
        print("Entering the room")
        self.room.enter_room()
        print("Lights are", self.room.lights)
        return self.room

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Leaving the room")
        self.room.leave_room()
        print("Lights are", self.room.lights)
        print("Exception:", exc_type)
        print("Done.")

он имеет 3 функции:

  • __init__ вызывается каждый раз, когда мы создаем контекст
  • __enter__ вызывается, когда мы начинаем использовать объект (это наша комната)
  • __exit__ вызывается один раз, когда мы перестаем использовать контекст

А как пользоваться диспетчером контекста? Это просто:

with RoomManager("John Smith") as room:
    print("  Watching a TV")

Здесь «комната» - это наш недавно созданный объект. Посмотрим, что у нас в консоли:

The room is ready
Entering the room
Lights are True
  Watching a TV
Leaving the room
Lights are False
Exception: None
Done.

Как видите, мы смотрим телевизор при включенном свете. Что приятно. И, в конце концов, свет выключен, потому что, как только мы выходим из контекста «с», наш диспетчер контекста вызывает функцию __exit__, которая, в свою очередь, вызывает функцию leave_room объекта.

Круто, не правда ли? Но что, если наш гость решил выпрыгнуть из окна? Давай проверим:

with RoomManager("John Smith") as room:
    print("  Watching a TV")
    room.jump_out_of_window()

Вот что мы получаем:

Есть исключение, ничего страшного. Но огни ложные. Это важно. Мы экономим энергию, и Грета Тунберг счастлива.

Что еще может сделать для нас менеджер контекста? Он может скрыть факт исключения. Потому что это может навредить репутации нашего отеля. И это можно сделать с помощью всего одной строчки кода: return True в функции __exit__:

def __exit__(self, exc_type, exc_val, exc_tb):
    print("Leaving the room")
    self.room.leave_room()
    print("Lights are", self.room.lights)
    print("Exception:", exc_type)
    print("Done.")
    return True  # Here is the change

Теперь мы получаем красивый результат:

Вы видите там строчку «Исключение»? Таким образом, мы можем обработать это исключение, мы можем вызвать полицию или закрыть окно, чтобы не тратить впустую тепловую энергию зимой. Грета Тунберг снова счастлива.

При создании этой статьи никто не пострадал. По крайней мере, так сказал наш менеджер по контексту.