time

Python и менеджер контекста

Наткнулся в интернете на интересный пример использования менеджера контекста и оператора with, но перед тем как я покажу что мне понравилось немного матчасти.

Менеджер контекста представляет собой объект, который создает контекст выполнения внутри оператора with и обладает двумя методами _enter_ и _exit_.

Простой пример использования менеджера контекста и оператора with:

class Logger(object):
    def __init__(self, val):
        self.val = val 
        print "%d :: __init__" % self.val

    def __enter__(self):
        print "%d :: __enter__" % self.val

    def __exit__(self, exc_type, exc_val, exc_tb):
        print "%d :: __exit__ " % self.val

for i in xrange(5):
    with  Logger(i) as log:
        pass

Истинное предназначения оператора with это замена подхода setup..try..except..finally:

do_init()

try :
    do_task()

except SomeError :
    do_exception_handling()

finally :
    do_exit()

Т.е. мы сначала инициализируем наш ресурс (метод _init_ объект менеджера контекста), потом выполняем попытку работы с нашим ресурсом в блоке try..except..finally (внутренний блок оператора with). Если возник ексепшен то попадаем в ветку except и обрабатываем ошибку (метод _exit_ менеджера контекста). По-окончанию выполняется ветка finally (метод _exit_ менеджера контекста) где производим зачистку и освобождение нашего ресурса.

Добавим обработчик ексепшенов в приведенный выше пример работы с менеджером контекста:

class Logger(object):
    def __init__(self, val):
        self.val = val
        print "%d :: __init__" % self.val

    def __enter__(self):
        print "%d :: __enter__" % self.val

    def __exit__(self, exc_type, exc_val, exc_tb):
        print "%d :: __exit__ " % self.val
        if exc_type:
            print "%d !! error " % self.val
            return True

for i in xrange(3):
    with  Logger(i) as log:
        if i == 1:
            raise ValueError(i)

Получим такой вывод:

0 :: __init__
0 :: __enter__
0 :: __exit__ 
1 :: __init__
1 :: __enter__
1 :: __exit__ 
1 !! error 
2 :: __init__
2 :: __enter__
2 :: __exit__ 

Это было беглое введение в менеджер контекста, более подробно можно ознакомится на docs.python.org или у sykora.

А теперь пример кода который мне понравился. Этот код показывает как можно начать работать с несколькими файлами используя менеджер контекст и одновременно с этим подчеркивает всю элегантность и наглядность кода на python, за что мы его и любим :). Причем мы можем не переживать за закрытие файлов и освобождение ресурсов (это уже реализовано внутри метода __exi\t__ класса mfw), просто передаем имя файлов и режим работы с ним.

class mfw:
    """
    mfw : Multi-File With
    This utility class is a list of file handles.  It implements  __enter__
    and __exit__ methods so that it can be used via the with clause.
    """
    def __init__(self, *args):
        self.handles = []
        for (the_file, mode) in args:
            self.handles.append(open(the_file, mode))

    def __enter__(self):
        return self.handles

    def __exit__(self, type, value, traceback):
        for handle in self.handles:
            handle.close()

with mfw(('f1.txt', 'r'), ('f2.txt', 'w')) as (infile, outfile):
    print infile.name
    print outfile.name

blog comments powered by Disqus