Python @precondition / @postcondition for member function - how?
我正在尝试对类的成员函数返回的值使用@postcondition装饰器,如下所示:
1 2 3 4 5 6 7 8 9 10 | def out_gt0(retval, inval): assert retval > 0,"Return value < 0" class foo(object): def __init__(self, w, h): self.width = w self.height = h @postcondition(out_gt0) def bar(self): return -1 |
当我尝试调用成员函数'bar'(并因此激发@postcondition提供警告)时,我得到了这个:
1 2 3 4 5 6 7 8 9 | >>> f = foo(2,3) >>> f.bar() Traceback (most recent call last): File"<pyshell#22>", line 1, in <module> f.bar() File"<pyshell#8>", line 106, in __call__ result = self._func(*args, **kwargs) TypeError: bar() takes exactly 1 argument (0 given) >>> |
我对@postcondition的定义是http://wiki.python.org/moin/PythonDecoratorLibrary#Pre-.2FPost-Conditions。
我认为错误的产生是因为作为@postcondition基础的函数不期望处理成员函数(当然我见过的所有例子都只是使用普通的旧函数)但是我不知道如何修复它所以我 可以这样做吗?
将不胜感激任何建议。
你不需要做任何特别的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import functools def condition(pre_condition=None, post_condition=None): def decorator(func): @functools.wraps(func) # presever name, docstring, etc def wrapper(*args, **kwargs): #NOTE: no self if pre_condition is not None: assert pre_condition(*args, **kwargs) retval = func(*args, **kwargs) # call original function or method if post_condition is not None: assert post_condition(retval) return retval return wrapper return decorator def pre_condition(check): return condition(pre_condition=check) def post_condition(check): return condition(post_condition=check) |
用法:
1 2 3 4 5 6 7 8 9 10 11 | @pre_condition(lambda arg: arg > 0) def function(arg): # ordinary function pass class C(object): @post_condition(lambda ret: ret > 0) def method_fail(self): return 0 @post_condition(lambda ret: ret > 0) def method_success(self): return 1 |
测试:
1 2 3 4 5 6 7 8 9 10 | function(1) try: function(0) except AssertionError: pass else: assert 0,"never happens" c = C() c.method_success() try: c.method_fail() except AssertionError: pass else: assert 0,"never happens" |
以下示例有效:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def out_gt0(retval): assert retval > 0,"Return value < 0" def mypostfunc(callback): def mydecorator(func): def retFunc(self, *args, **kwargs): retval = func(self, *args, **kwargs) callback(retval) return retval return retFunc return mydecorator class foo(object): def __init__(self, w, h): self.width = w self.height = h @mypostfunc(out_gt0) def bar1(self): return -1 @mypostfunc(out_gt0) def bar2(self): return 1 f=foo(1,2) print"bar2:", f.bar2() print"bar1:", f.bar1() |
输出是:
1 2 3 4 5 6 7 8 9 10 | bar2: 1 bar1: Traceback (most recent call last): File"s.py", line 27, in <module> print"bar1:", f.bar1() File"s.py", line 9, in retFunc callback(retval) File"s.py", line 3, in out_gt0 assert retval > 0,"Return value < 0" AssertionError: Return value < 0 |