代码之家  ›  专栏  ›  技术社区  ›  David Schoonover

可作为InstanceMethod调用?

  •  2
  • David Schoonover  · 技术社区  · 17 年前

    假设我们有一个元类 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.MethodType types.UnboundMethodType 哪儿也没去。稍微反省一下,他们似乎从 type . 所以我试着将两者都作为元类使用,但也没有运气。他们作为一个元类可能有特殊的需求,但目前看来我们还处于非法领域。

    有什么想法吗?

    4 回复  |  直到 17 年前
        1
  •  3
  •   Alex Martelli    17 年前

    只是充实你 InstanceMethodWrapper 用A类 __get__ (完全可以 return self )--也就是说,把那个班变成 描述符 类型,使其实例成为描述符对象。见 http://users.rcn.com/python/download/Descriptor.htm 背景和细节。

    顺便说一句,如果您使用的是python 2.6或更高版本,请考虑使用类修饰器而不是该元类——我们添加了类修饰器,正是因为如此多的元类正被用于此类修饰目的,而修饰器的使用实际上要简单得多。

        2
  •  0
  •   David Schoonover    17 年前

    编辑 我又撒谎了。这个 __?attr__ 函数的属性是只读的,但显然不总是抛出 AttributeException 分配时出现异常?我不知道。回到一号广场!

    编辑 :这实际上并不能解决问题,因为包装函数不会将属性请求代理到 InstanceMethodWrapper . 当然,我可以用鸭子打 是吗?阿特拉菲 装饰器的属性——这就是我现在所做的——但这很难看。更好的想法是非常受欢迎的。


    当然,我立刻意识到将一个简单的装饰器与我们的类结合起来就可以做到这一点:

    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
  •  0
  •   Unknown    17 年前

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

    这是我的版本,我认为它有点不那么倾斜。

    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
  •  0
  •   Alex Coventry    17 年前

    我认为你需要对你的问题更具体一些。最初的问题是关于包装一个函数,但您随后的回答似乎是关于保存函数属性,这似乎是一个新的因素。如果您更清楚地阐明了您的设计目标,那么回答您的问题可能会更容易。