Python装饰器类中的__enter__和__exit__是如何工作的?

43

我正在尝试创建一个装饰器类来计算函数被调用的次数,但是我收到了一个错误信息,上面写着:

    "TypeError: __exit__() takes exactly 1 argument (4 given)"

我真的不知道我是如何给它传递四个参数的。我的代码看起来像这样:

class fcount2(object):
    __instances = {}
    def __init__(self, f):
        self.__f = f
        self.__numcalls = 0
        fcount2.__instances[f] = self

    def __call__(self, *args, **kwargs):
        self.__numcalls += 1
        return self.__f(*args, **kwargs)

    def __enter__(self):
        return self

    def __exit__(self):
        return self

    @staticmethod
    def count(f):
        return fcount2.__instances[self.__f].__numcalls


@fcount2
def f(n):
    return n+2

for n in range(5):
    print f(n)   
print 'f count =',f.count

def foo(n):
    return n*n

with fcount2(foo) as g:
    print g(1)
    print g(2)
print 'g count =',g.count
print 'f count =',f.count

with fcount2(f) as g:
    print g(1)
    print g(2)
print 'g count =',g.count
print 'f count =',f.count

with f:
    print f(1)
    print g(2)
print 'g count =',g.count
print 'f count =',f.count

我是否需要传递其他参数到 exit 函数中?或者不应该传递一些参数?如果您有任何提示或想法,请告诉我。

顺便说一下,我的代码行 "print 'f count =',f.count" 看起来输出的是内存地址而不是值,但这是完全不同的问题。


1
你希望上下文管理器具有什么行为?换句话说,装饰器和上下文管理器是两个完全不同的东西,用途也不同...例如,你为什么要在这里创建一个上下文管理器? - mgilson
我试图为“with”调用函数的次数和总体调用函数的次数保持单独的计数器。 - user3402743
2个回答

77

__exit__()方法应接受有关在with:块中遇到的异常的信息。请参见此处

以下修改后的代码可正常运行:

def __exit__(self, exc_type, exc_value, tb):
    if exc_type is not None:
        traceback.print_exception(exc_type, exc_value, tb)
        # return False # uncomment to pass exception through

    return True

然后你可以尝试在你的with:块中触发一个异常,它将会被__exit__()捕获。


7
你获得该错误的原因是因为__exit __()除了self外还需要三个参数,分别是:

  1. 异常类型
  2. 异常值
  3. 回溯信息

你可以有两种方式定义__exit__()方法:

def __exit__(self, exception_type, exception_value, traceback):

或者
def __exit__(self, *args, **kwargs):

with语句支持通过使用实现了__enter__()__exit__()方法对的上下文管理器来定义运行时上下文的概念。请访问此链接获取详细信息。

__enter__()方法将进入运行时上下文并返回与运行时上下文相关的对象本身或其他对象。

__exit__()方法将退出运行时上下文,并返回一个布尔标志,指示是否应该抑制任何发生的异常。这些参数的值包含有关抛出的异常的信息。如果这些值等于None,则表示没有抛出异常。


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