【问题标题】:callable as instancemethod?可作为实例方法调用?
【发布时间】:2010-10-23 09:22:18
【问题描述】:

假设我们有一个元类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 下降。所以我尝试将两者都用作元类,但也没有运气。可能是他们对元类有特殊要求,但目前我们似乎处于无证领域。

有什么想法吗?

【问题讨论】:

  • 你能解释一下吗?我无法理解您要执行的操作。

标签: python methods metaclass python-descriptors


【解决方案1】:

只需用__get__ 丰富您的InstanceMethodWrapper 类(也可以是return self)——也就是说,将该类设为描述符 类型,以便其实例为描述符对象。有关背景和详细信息,请参阅http://users.rcn.com/python/download/Descriptor.htm

顺便说一句,如果您使用的是 Python 2.6 或更高版本,请考虑使用类装饰器而不是那个元类——我们添加类装饰器正是因为有很多元类被用于此类装饰目的,而装饰器真的很多使用更简单。

【讨论】:

【解决方案2】:

编辑:我又撒谎了。函数上的__?attr__ 属性是只读的,但显然在分配时并不总是抛出AttributeException 异常?我不知道。回到第一格!

编辑:这实际上并不能解决问题,因为包装函数不会将属性请求代理到InstanceMethodWrapper。当然,我可以在装饰器中对__?attr__ 属性进行打鸭式攻击——这就是我现在正在做的事情——但这很丑陋。我们非常欢迎更好的想法。


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

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

然后将装饰器添加到元类中对InstanceMethodWrapper 的调用中:

cls_dict[k] = methodize(v, InstanceMethodWrapper(v))

噗。有点倾斜,但它有效。

【讨论】:

    【解决方案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")
    

    【讨论】:

      【解决方案4】:

      我认为您需要更具体地了解您的问题。最初的问题是关于包装函数,但您随后的答案似乎是在谈论保留函数属性,这似乎是一个新因素。如果您更清楚地说明您的设计目标,可能会更容易回答您的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-09
        • 2010-11-04
        • 2016-08-30
        • 2010-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多