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

Имам тестов клас, който изисква да направя малко почистване в края. За да съм сигурен, че потребителят няма да забрави да направи това, искам да добавя контекстен мениджър към класа. Имам и декоратор, вътре в който искам да използвам този контекстен мениджър, за да създам обект от тестов клас и да го предам на декорираната функция. Възможно ли е изобщо?

Ето какво искам да направя:

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