动态地将callable添加到类作为实例“方法”

时间:2013-01-25 16:58:35

标签: python class methods callable

我实现了一个元类,它删除了用它创建的类的类属性,并从这些参数的数据构建方法,然后将这些动态创建的方法直接附加到类对象(有问题的类允许轻松定义Web表单对象,用于Web测试框架)。它已经工作得很好,但现在我需要添加一个更复杂的方法类型,为了保持干净,我实现了一个可调用的类。不幸的是,当我尝试在一个实例上调用可调用类时,它被视为一个类属性而不是实例方法,并且在被调用时,只接收它自己的self。我可以看出为什么会发生这种情况,但我希望有人可能比我提出的更好的解决方案。问题的简化说明:

class Foo(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val
        self.__name__ = name + '_foo'
        self.name = name
    # This doesn't work as I'd wish
    def __call__(self, instance):
        return self.name + str(self.val + instance.val)

def get_methods(name, foo_val):
    foo = Foo(name, foo_val)
    def bar(self):
        return name + str(self.val + 2)
    bar.__name__ = name + '_bar'
    return foo, bar

class Baz(object):
    def __init__(self, val):
        self.val = val

for method in get_methods('biff', 1):
    setattr(Baz, method.__name__, method)
baz = Baz(10)
# baz.val == 10
# baz.biff_foo() == 'biff11'
# baz.biff_bar() == 'biff12'

我想到了:

  1. 使用描述符,但这似乎比这里必要的更复杂
  2. foo的工厂内使用闭包,但嵌套的闭包是大多数时候对象的丑陋和凌乱的替换,imo
  3. Foo实例包含在将self向下传递给Foo实例instance的方法中,基本上是一个装饰器,这就是我实际添加到{ {1}},但这似乎是多余的,基本上只是一种更复杂的方式来做同样的事情(2)
  4. 有没有比这些更好的方法来尝试完成我想要的东西,或者我应该咬紧牙关并使用一些封闭工厂类型模式?

2 个答案:

答案 0 :(得分:4)

执行此操作的一种方法是将可调用对象作为未绑定方法附加到类。方法构造函数使用任意可调用对象(即具有__call__()方法的类的实例) - 而不仅仅是函数。

from types import MethodType

class Foo(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val
        self.__name__ = name + '_foo'
        self.name = name
    def __call__(self, instance):
        return self.name + str(self.val + instance.val)

class Baz(object):
    def __init__(self, val):
        self.val = val

Baz.biff = MethodType(Foo("biff", 42), None, Baz)

b = Baz(13)
print b.biff()
>>> biff55

在Python 3中,没有未绑定的实例(类只附加了常规函数),因此您可以改为使Foo类成为一个描述符,通过赋予它{{1 }} 方法。 (实际上,这种方法也适用于Python 2.x,但上面的方法会更好一些。)

__get__()

答案 1 :(得分:1)

你遇到的麻烦是你的对象没有被绑定为你所使用的Baz类的方法。这是因为它不是描述符,which regular functions are

您可以通过向__get__类添加一个简单的Foo方法来解决此问题,该方法在将其作为描述符进行访问时将其作为方法:

import types

class Foo(object):
    # your other stuff here

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self # unbound
        else:
            return types.MethodType(self, obj) # bound to obj