7

我实现了一个元类,它拆除了用它创建的类的类属性,并从这些参数的数据中构建方法,然后将这些动态创建的方法直接附加到类对象(有问题的类允许轻松定义 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在一个方法中,将其传递selfFoo实例instance,基本上是一个装饰器,这就是我实际添加的Baz,但这似乎是多余的,基本上只是一种更复杂的方式来做与 (2) 相同的事情

有没有比这些更好的方法来尝试完成我想要的,还是我应该硬着头皮使用一些闭包工厂类型模式?

4

2 回答 2

6

一种方法是将可调用对象作为未绑定方法附加到类。方法构造函数与任意可调用对象(即具有__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
于 2013-01-25T17:45:04.503 回答
1

您遇到的问题是您的对象没有被绑定为您要放入的 Baz 类的方法。这是因为它不是描述符,常规函数是

您可以通过向您的类添加一个简单的__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
于 2013-01-25T17:49:43.947 回答