可调用为instancemethod?

2024-10-01 02:26:27 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我们有一个元类CallableWrappingMeta,它遍历一个新类的主体,用一个类InstanceMethodWrapper包装它的方法:

import types

class CallableWrappingMeta(type):
    def __new__(mcls, name, bases, cls_dict):
        for k, v in cls_dict.iteritems():
            if isinstance(v, types.FunctionType):
                cls_dict[k] = InstanceMethodWrapper(v)
        return type.__new__(mcls, name, bases, cls_dict)

class InstanceMethodWrapper(object):
    def __init__(self, method):
        self.method = method
    def __call__(self, *args, **kw):
        print "InstanceMethodWrapper.__call__( %s, *%r, **%r )" % (self, args, kw)
        return self.method(*args, **kw)

class Bar(object):
    __metaclass__ = CallableWrappingMeta
    def __init__(self):
        print 'bar!'

我们的虚拟包装器只是在参数进来时打印它们。但是您会注意到一些明显的事情:方法没有被传递给实例对象接收者,因为即使InstanceMethodWrapper是可调用的,但在类创建过程中(在我们的元类处理完之后),它也不会被视为一个函数来转换为实例方法。在

一个潜在的解决方案是使用修饰符而不是类来包装方法——该函数将成为一个实例方法。但在现实世界中,InstanceMethodWrapper要复杂得多:它提供一个API并发布方法调用事件。一个类更方便(也更高效,这并不重要)。在

我也试过一些死胡同。子类化types.MethodTypetypes.UnboundMethodType没有任何进展。稍微反省一下,他们似乎从type开始堕落。所以我试着用这两种方法作为元类,但也没什么好运气的。也许他们对元类有特殊的要求,但现在看来我们已经进入了无文档的领域。在

有什么想法吗?在


Tags: 实例方法selfnewdeftypeargsmethod
3条回答

我猜你是在尝试创建一个元类,用一个自定义函数包装类中的每个方法。在

这是我的版本,我觉得不那么偏激。在

import types

class CallableWrappingMeta(type):
    def __new__(mcls, name, bases, cls_dict):
        instance = type.__new__(mcls, name, bases, cls_dict)
        for k in dir(instance):
            v = getattr(instance, k)
            if isinstance(v, types.MethodType):
                setattr(instance, k, instanceMethodWrapper(v))

        return instance

def instanceMethodWrapper(function):
    def customfunc(*args, **kw):
        print "instanceMethodWrapper(*%r, **%r )" % (args, kw)
        return function(*args, **kw)
    return customfunc

class Bar(object):
    __metaclass__ = CallableWrappingMeta

    def method(self, a, b):
        print a,b

a = Bar()
a.method("foo","bar")

编辑:我又撒谎了。函数的__?attr__属性是只读的,但显然在赋值时并不总是抛出AttributeException异常?我不知道。回到原点!在

Edit:这并不能真正解决问题,因为包装函数不会将属性请求代理到InstanceMethodWrapper。当然,我可以在decorator中对__?attr__属性进行打孔——这就是我现在所做的——但这很难看。更好的主意是非常受欢迎的。在


当然,我马上意识到,将一个简单的decorator与我们的类结合起来就可以做到:

def methodize(method, callable):
    "Circumvents the fact that callables are not converted to instance methods."
    @wraps(method)
    def wrapper(*args, **kw):
        return wrapper._callable(*args, **kw)
    wrapper._callable = callable
    return wrapper

然后将decorator添加到元类中对InstanceMethodWrapper的调用:

^{pr2}$

噗。有点不对劲,但很管用。在

只需使用一个__get__(它可以很好地return self)来充实你的InstanceMethodWrapper也就是说,把这个类变成一个描述符类型,这样它的实例就是描述符对象。有关背景和详细信息,请参见http://users.rcn.com/python/download/Descriptor.htm。在

顺便说一句,如果您使用的是Python2.6或更高版本,请考虑使用一个类decorator而不是那个元类——我们添加类decorator正是因为有太多的元类被用于这样的装饰目的,而decorator的使用真的要简单得多。在

相关问题 更多 >