动态地添加到可调用类作为实例“方法”(dynamically adding callable to

2019-07-18 17:51发布

我实施了扯下用于与它创建的类的类的属性和构建从这些参数的数据的方法元类,然后直接连接的那些动态创建的方法对类对象(所讨论的类允许用于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 ,基本上是一个装饰,这也是我实际添加到Baz ,但这似乎是多余的,基本上只是在做同样的事情更复杂的方法( 2)

有没有更好的方法,然后任何一个试图完成我想要的东西,或者我应该只是咬紧牙关,使用一些封闭工厂型模式?

Answer 1:

要做到这一点的方法之一是可调用的对象附以班级为未绑定的方法。 该方法构造函数任意可调用(即一个类的实例工作__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类,给予它返回一个绑定的实例方法描述__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


Answer 2:

您正在运行到麻烦的是,你的对象没有被绑定为你把它在巴兹类的方法,这是因为它不是一个描述符, 其中常规功能 !

您可以通过添加一个简单的解决这个问题__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


文章来源: dynamically adding callable to class as instance “method”