Python @precondition/@postcondition для функции-члена - как?
Я пытаюсь использовать декоратор @postcondition для значения, возвращаемого функцией-членом класса, например:
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 на предоставление предупреждения), я получаю следующее:
>>> 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
Вам не нужно ничего особенного делать:
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)
Использование:
@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
Тест:
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"
Ответ 2
пример ниже работает:
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()
а выход:
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