如何在装饰器中使用上下文管理器以及如何将在decorator中创建的对象传递给装饰函数

Roh*_*han 3 python contextmanager python-decorators

我有一个测试类,需要在最后进行一些清理.为了确保用户不会忘记这样做,我想在类中添加一个上下文管理器.我还有一个装饰器,我想在其中使用此上下文管理器来创建测试类的对象并将其传递给装饰函数.它甚至可能吗?

这就是我要做的事情:

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")
Run Code Online (Sandbox Code Playgroud)

该函数testf接受测试类对象 - testobj并用它做事.之后,由于上下文管理器,testcm确保调用清理函数.

所以有两个问题:

  1. 我如何在装饰器中使用上下文管理器,从我所知的装饰器必须返回一个函数,但如果我返回该函数(如上面的代码),上下文管理器将如何调用清理?

  2. 如何将装饰器中创建的对象传递给装饰函数,如果我像上面的代码一样传递它,我将如何调用装饰函数?

Rob*_*obᵩ 5

你的示例程序中有几个错误,我在所有test/ testf/ testobj冗余中丢失了.请允许我直接解决您的问题.

如何在装饰器中使用上下文管理器?

正如您在其他任何地方使用上下文管理器一样.考虑这个程序,它使用装饰器在调用函数时透明地转换strfile:

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')
Run Code Online (Sandbox Code Playgroud)

如您所见,装饰器函数使用围绕调用函数调用的上下文管理器.

如何将装饰器中创建的对象传递给装饰函数,如果我像上面的代码一样传递它,我将如何调用装饰函数?

正如您将对象传递给任何函数一样.见上面的例子.装饰器创建一个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()
Run Code Online (Sandbox Code Playgroud)