Python@premission/@postcondition的成员函数-如何?

Python@premission/@postcondition的成员函数-如何?,python,decorator,Python,Decorator,我试图对类的成员函数返回的值使用@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 b

我试图对类的成员函数返回的值使用@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
def out_gt0(返回,无效):
断言retval>0,“返回值<0”
类foo(对象):
定义初始值(self,w,h):
自宽=w
自高度=h
@后置条件(输出gt0)
def bar(自):
返回-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)
>>> 
f=foo(2,3) >>>f.bar() 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 f、 bar() 文件“”,第106行,在调用中__ 结果=自身函数(*args,**kwargs) TypeError:bar()正好接受1个参数(给定0) >>> 我对@postcondition的定义就是这里看到的

我假设出现错误是因为@postcondition的基础函数不希望处理成员函数(当然,我见过的所有示例都只是使用普通的旧函数),但我不确定如何修复它,以便可以这样做

如有任何建议,我们将不胜感激。

以下示例有效:

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()
def out_gt0(返回):
断言retval>0,“返回值<0”
def mypostfunc(回调):
def mydecorator(func):
def retFunc(self,*args,**kwargs):
retval=func(self,*args,**kwargs)
回调(retval)
返回返回
返回函数
返回我的装饰器
类foo(对象):
定义初始值(self,w,h):
自宽=w
自高度=h
@mypostfunc(输出gt0)
def bar1(自身):
返回-1
@mypostfunc(输出gt0)
def bar2(自身):
返回1
f=foo(1,2)
打印“bar2:”,f.bar2()
打印“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
bar2:1
bar1:
回溯(最近一次呼叫最后一次):
文件“s.py”,第27行,在
打印“bar1:”,f.bar1()
文件“s.py”,第9行,在retFunc中
回调(retval)
文件“s.py”,第3行,输入输出
断言retval>0,“返回值<0”
AssertionError:返回值<0

您不需要做任何特殊的事情:

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
测试:


返回的
FunctionWrapper
类不知道如何正确绑定到实例(它缺少所需的
\uuuu get\uuuu
方法,请参见)。这是因为修饰后的函数不再是函数,而是可调用的对象。顺便说一句,这个函数实现看起来很糟糕(而且不太和谐)…我认为这是因为你修饰了方法——而不仅仅是函数——如果我是你,那么我只会使用我自己的修饰器。这里有一个很好的方法——如果你想运行你的示例,只需运行
f.bar(f)
,它也应该可以工作(如果
f.bar
将有参数,则始终将该对象推到第一个位置-这将是函数中的
self
)之所以有效,是因为现在decorator是一个函数对象,它确实实现了
\u get\u
描述符挂钩。
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"