Как использовать контекстный менеджер внутри декоратора и как передать объект, созданный в декораторе, в декоративную функцию

У меня есть тестовый класс, который требует очистки в конце. Чтобы убедиться, что пользователь не забудет это сделать, я хочу добавить в класс контекстный менеджер. У меня также есть декоратор, внутри которого я хочу использовать этот менеджер контекста, чтобы создать объект тестового класса и передать его декорированной функции. Это вообще возможно?

Вот что я хочу сделать:

class test:
    def __init__(self, name):
        self._name = name
        print "my name is {0}".format(name)

    def exit():
        print "exiting"

    @contextmanager
    def testcm(self):
        print "inside cm"
        try:
            yield self
        finally:
            self.exit()

    def randomtest(self, str):
        print "Inside random test {0}".format(str)


def decorate(name):
    def wrapper(testf):
        def testf_wrapper(test):
            with test(name).testcm() as testobj:
                return testf(testobj)
            return testf_wrapper
        return wrapper
    return decorate

@decorate("whatever")
def testf(testobj):
    testobj.randomtest("randomness")

Функция testf берет объект тестового класса - testobj и делает с ним что-то. Затем, благодаря диспетчеру контекста, testcm обеспечивает вызов функции очистки.

Итак, есть два вопроса:

  1. Как мне использовать контекстный менеджер внутри декоратора, из того, что я знаю, декоратор должен возвращать функцию, но если я верну функцию (как в коде выше), как контекстный менеджер вызовет очистку?

  2. Как передать объект, созданный в декораторе, в декоративную функцию, если я передам его, как в приведенном выше коде, как мне вызвать декорированную функцию?


person ocwirk    schedule 20.05.2015    source источник


Ответы (1)


В вашей примерной программе есть несколько ошибок, и я потерялся во всей избыточности test/testf/testobj. Позвольте мне ответить на ваши вопросы напрямую.

Как использовать контекстный менеджер внутри декоратора?

Точно так же, как вы использовали бы контекстный менеджер в любом другом месте. Рассмотрим эту программу, которая использует декоратор для прозрачного преобразования str в file при вызове функции:

def opener(func):
    def wrapper(name):
        with open(name) as input_file:
            func(input_file)
    return wrapper

@opener
def first_line(fd):
    print fd.readline()

first_line('/etc/passwd')

Как видите, функция декоратора использует диспетчер контекста для вызова декорированной функции.

Как передать объект, созданный в декораторе, в декоративную функцию, если я передам его, как в приведенном выше коде, как мне вызвать декорированную функцию?

Точно так же, как вы передаете объект любой функции. См. мой пример выше. Декоратор создает объект file и передает его декорируемой функции.


Для полноты картины вот ваша примерная программа с исправленными ошибками:

from contextlib import contextmanager

class test:
    def __init__(self, name):
        self._name = name
        print "my name is {0}".format(name)

    def exit(self):
        print "exiting"

    @contextmanager
    def testcm(self):
        print "inside cm"
        try:
            yield self
        finally:
            self.exit()

    def randomtest(self, str):
        print "Inside random test {0}".format(str)


def decorate(name):
    def wrapper(testf):
        def testf_wrapper():
            with test(name).testcm() as testobj:
                return testf(testobj)
        return testf_wrapper
    return wrapper

@decorate("whatever")
def testf(testobj):
    testobj.randomtest("randomness")


testf()
person Robᵩ    schedule 20.05.2015