关于装饰器:成员函数的Python @precondition / @postcondition – 怎么样?

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