动态地将可调用的类添加为类的“方法”

Sil*_*Ray 5 python methods class callable

我实现了一个元类,该元类拆解使用它创建的类的类属性,并根据这些参数中的数据构建方法,然后将这些动态创建的方法直接附加到类对象上(所涉及的类可轻松定义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'
Run Code Online (Sandbox Code Playgroud)

我想到了:

  1. 使用描述符,但是看起来比这里需要的复杂得多
  2. fooimo 在工厂内部使用闭包,但嵌套闭包在大多数情况下都是丑陋且凌乱的对象替换
  3. Foo实例包装为一种方法,将其作为实例传递selfFoo实例(instance基本上是装饰器),这实际上是我真正添加的内容Baz,但这似乎是多余的,并且基本上只是一种与(2)相同的更复杂的方法

有没有比这些方法中的任何一种更好的方法来尝试实现我想要的功能,还是我应该硬着头皮使用某种闭包工厂类型模式?

kin*_*all 5

一种方法是将可调用对象作为未绑定方法附加到类。方法构造函数与任意可调用对象(即具有__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
Run Code Online (Sandbox Code Playgroud)

在Python 3中,没有未绑定实例(类仅附加了常规函数)之类的东西,因此您可以改为通过给Foo类提供一个方法,使类成为返回绑定实例方法的描述符__get__()。(实际上,该方法也可以在Python 2.x中使用,但是上面的方法会更好一些。)

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)
    def __get__(self, instance, owner):
        return MethodType(self, instance) if instance else self
        # Python 2: MethodType(self, instance, owner)

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

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

b = Baz(13)
print b.biff()
>>> biff55
Run Code Online (Sandbox Code Playgroud)