Python中的Doctest和装饰器

5 投票
2 回答
1206 浏览
提问于 2025-04-18 01:22

我在尝试用Python的装饰器来捕捉异常并记录这些异常。

import os.path
import shutil

class log(object):
    def __init__(self, f):
        print "Inside __init__()"
        self.f = f

    def __call__(self, *args):
        print "Inside __call__()"
        try:
            self.f(*args)
        except Exception:
            print "Sorry"

@log
def testit(a, b, c):
    print a,b,c
    raise RuntimeError()

if __name__ == "__main__":
    testit(1,2,3)

这样做效果很好。

Desktop> python deco.py 
Inside __init__()
Inside __call__()
1 2 3
Sorry

但问题是,当我想用doctest来测试的时候,

@log
def testit(a, b, c):
    """
    >>> testit(1,2,3)
    """
    print a,b,c
    raise RuntimeError()

if __name__ == "__main__":
    import doctest
    doctest.testmod()

似乎什么都没有发生。

Desktop> python deco2.py 
Inside __init__()

这是什么原因呢?

2 个回答

6

你需要把文档字符串复制到你的装饰器里:

class log(object):
    def __init__(self, f):
        print "Inside __init__()"
        self.f = f
        self.__doc__ = f.__doc__

    def __call__(self, *args):
        print "Inside __call__()"
        try:
            self.f(*args)
        except Exception:
            print "Sorry"

这个装饰器会“替代”被装饰的函数,只有通过复制文档字符串,其他人才能看到这个属性。

你可以使用 functools.update_wrapper() 来帮你复制文档字符串,还有其他一些属性:

from functools import update_wrapper

class log(object):
    def __init__(self, f):
        print "Inside __init__()"
        self.f = f
        update_wrapper(self, f)

    def __call__(self, *args):
        print "Inside __call__()"
        try:
            self.f(*args)
        except Exception:
            print "Sorry"
9

被装饰的函数(其实是一个类的实例)并没有继承原始函数的 __doc__ 属性(这是 doctest 解析的内容)。你可以选择把 __doc__ 属性复制到这个类的实例上,但……老实说,我觉得这里根本不需要用到类,直接使用 functools.wraps 可能会更好。

import functools
def log(func):
    @functools.wraps(func)
    def wrapper(*args):
        try:
            return func(*args)
        except Exception:
            print "sorry"
    return wrapper

撰写回答