从装饰器访问拥有装饰方法的类

时间:2009-04-15 20:23:36

标签: python decorator

我正在为必须检查父方法的方法编写装饰器(在我正在装饰的类的父级中使用相同名称的方法)。

示例(来自PEP 318的第四个示例):

def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            assert isinstance(result, rtype), \
                   "return value %r does not match %s" % (result,rtype)
            return result
        new_f.func_name = f.func_name
        # here I want to reach the class owning the decorated method f,
        # it should give me the class A
        return new_f
    return check_returns

class A(object):
    @returns(int)
    def compute(self, value):
        return value * 3

所以我正在寻找代码来代替#here here here

感谢。

2 个答案:

答案 0 :(得分:6)

  

这里我想要达到拥有装饰方法f的类

你不能因为在装饰时,没有类拥有方法f。

class A(object):
    @returns(int)
    def compute(self, value):
        return value * 3

与说法相同:

class A(object):
    pass

@returns(int)
def compute(self, value):
    return value*3

A.compute= compute

显然,returns()装饰器是在将函数分配给所有者类之前构建的。

现在,当您将一个函数写入类(内联或显式类似)时,它将成为未绑定的方法对象。 现在它有一个对它的所有者类的引用,你可以这样说:

>>> A.compute.im_class
<class '__main__.A'>

所以你可以在'new_f'中读取f.im_class,它在赋值后执行,但不在装饰器本身中执行。

(即便如此,如果你不需要,依赖CPython实现细节有点难看。我不太确定你要做什么,但涉及“获得所有者类”的事情往往是可以使用元类。)

答案 1 :(得分:6)

作为bobince said it,您无法访问周围的类,因为在调用装饰器时,该类尚不存在。如果您需要访问班级和基础的完整字典,您应该考虑metaclass

  

__metaclass__

     

此变量可以是name,bases和dict的任何可调用接受参数。在创建类时,使用callable而不是内置类型()。

基本上,我们将returns装饰器转换成只告诉元类在类构造上做一些魔术的东西:

class CheckedReturnType(object):
    def __init__(self, meth, rtype):
        self.meth = meth
        self.rtype = rtype

def returns(rtype):
    def _inner(f):
        return CheckedReturnType(f, rtype)
    return _inner

class BaseInspector(type):
    def __new__(mcs, name, bases, dct):
        for obj_name, obj in dct.iteritems():
            if isinstance(obj, CheckedReturnType):
                # do your wrapping & checking here, base classes are in bases
                # reassign to dct
        return type.__new__(mcs, name, bases, dct)

class A(object):
    __metaclass__ = BaseInspector
    @returns(int)
    def compute(self, value):
        return value * 3

请注意,我还没有测试过此代码,如果我要更新此代码,请发表评论。

强烈推荐的David Mertz有一些articles on metaclasses,你可能会在这个背景下感兴趣。