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

4 投票
2 回答
608 浏览
提问于 2025-04-15 11:08

我正在为一些方法写一个装饰器,这些方法需要检查它们的父类方法(也就是在我装饰的类的父类中同名的方法)。

举个例子(来自于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

所以我在寻找可以替代# 这里我想要...的代码。

谢谢。

2 个回答

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 的实现细节也有点不太好。我不太确定你想要做什么,但涉及“获取拥有类”的事情,通常可以通过元类来实现。)

6

正如bobince所说,你无法访问周围的类,因为在装饰器被调用时,类还不存在。如果你需要访问类的完整字典和基类,你可以考虑使用 metaclass(元类)

__metaclass__

这个变量可以是任何一个可以接受名称、基类和字典作为参数的可调用对象。在创建类的时候,这个可调用对象会替代内置的type()。

基本上,我们把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,你可能会觉得这些内容在这个上下文中很有趣。

撰写回答