如何在装饰器内使用上下文管理器并将在装饰器中创建的对象传递给被装饰函数。

3
我有一个测试类,需要在结束时进行一些清理。为了确保用户不会忘记这一点,我想在类中添加上下文管理器。我还有一个装饰器,在其中我想使用此上下文管理器创建一个测试类对象并将其传递给装饰的函数。这是否可能?
我想要做的就是:
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. 我如何将在装饰器中创建的对象传递给被装饰的函数?如果我像上面的代码一样传递它,我该如何调用被装饰的函数?

1个回答

7
您的示例程序有多个错误,并且我在所有的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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接