Python 动态添加可调用类作为实例";方法“;

Python 动态添加可调用类作为实例";方法“;,python,class,methods,callable,Python,Class,Methods,Callable,我实现了一个元类,该元类可以删除用它创建的类的类属性,并从这些参数的数据中构建方法,然后将这些动态创建的方法直接附加到类对象(该类允许轻松定义web测试框架中使用的web表单对象)。它一直工作得很好,但现在我需要添加一种更复杂的方法,为了保持整洁,我将其实现为一个可调用类。不幸的是,当我尝试调用实例上的可调用类时,它被视为类属性而不是实例方法,并且在调用时,只接收它自己的self。我知道为什么会这样,但我希望有人能找到比我想出的更好的解决方案。问题的简化说明: class Foo(object)

我实现了一个元类,该元类可以删除用它创建的类的类属性,并从这些参数的数据中构建方法,然后将这些动态创建的方法直接附加到类对象(该类允许轻松定义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'
我想到了:

  • 使用描述符,但这似乎比这里需要的复杂得多
  • imo说,在工厂内部为
    foo
    使用闭包,但是嵌套闭包在大多数情况下都是丑陋而混乱的对象替换
  • Foo
    实例包装在一个方法中,该方法将其
    自身
    传递给
    Foo
    实例作为
    实例
    ,基本上是一个装饰器,这是我实际添加到
    Baz
    中的内容,但这似乎是多余的,基本上只是一种与(2)相同的更复杂的方法

  • 有没有比这些更好的方法来实现我想要的,或者我应该咬紧牙关,使用某种闭包工厂类型模式呢?

    一种方法是将可调用对象作为未绑定方法附加到类上。方法构造函数将处理任意可调用项(即具有
    \uuuu call\uuuu()
    方法的类的实例),而不仅仅是函数

    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
    
    在Python3中,没有未绑定实例(类只是附加了常规函数),因此您可以将
    Foo
    类作为一个描述符,通过给它一个
    \uuuu get\uuuu()
    方法来返回绑定实例方法。(实际上,这种方法在Python2.x中也可以使用,但是上面的方法性能会更好一些。)


    您遇到的问题是,您的对象没有被绑定为所放入的Baz类的方法。这是因为它不是一个描述符

    您可以通过向
    Foo
    类添加一个简单的
    \uuuuu get\uuuu
    方法来解决此问题,该方法在作为描述符访问时使其成为一个方法:

    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
    

    这在Python 3中不起作用,因为未绑定的方法不再存在(
    MethodType
    只接受两个参数,第二个参数不能是None)。我想在您编写注释时我正在更新它。:-)我以前写过描述符,但我想在这种情况下,描述符没有那么凌乱,真的。。。我以前曾尝试过
    MethodType
    ,但老实说,它总是让我感到害怕,因为它似乎很容易被破坏,需要基本上初始化它的名称空间等等。我最终实现了另一个闭包,但我可能会回头看看用这个来替换它。谢谢。
    MethodType
    真的没有那么复杂。您需要一个函数(或其他可调用函数)、一个实例(如果您正在创建实例方法;在Python3中,这是唯一可以创建的类型)和一个类型(在Python2.x中;在Python3中,它从实例获取类型)。就是这样。我认为您不需要在其中使用
    if/else
    ,因为当通过类访问
    Foo
    实例时,
    \uu get\uu()
    不会被调用。@kindall:如果我没有
    if
    块,我通过类访问
    Foo
    实例时遇到异常:
    TypeError:self不能为None
    。这就是Python 3中仍然存在的“类绑定”,尽管函数不再使用它来生成未绑定的方法。
    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