简体   繁体   English

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

[英]dynamically adding callable to class as instance “method”

I implemented a metaclass that tears down the class attributes for classes created with it and builds methods from the data from those arguments, then attaches those dynamically created methods directly to the class object (the class in question allows for easy definition of web form objects for use in a web testing framework). 我实现了一个元类,它删除了用它创建的类的类属性,并从这些参数的数据构建方法,然后将这些动态创建的方法直接附加到类对象(有问题的类允许轻松定义Web表单对象在Web测试框架中使用)。 It has been working just fine, but now I have a need to add a more complex type of method, which, to try to keep things clean, I implemented as a callable class. 它已经工作得很好,但现在我需要添加一个更复杂的方法类型,为了保持干净,我实现了一个可调用的类。 Unfortunately, when I try to call the callable class on an instance, it is treated as a class attribute instead of an instance method, and when called, only receives its own self . 不幸的是,当我尝试在一个实例上调用可调用类时,它被视为一个类属性而不是一个实例方法,并且在被调用时,只接收它自己的self I can see why this happens, but I was hoping someone might have a better solution than the ones I've come up with. 我可以看出为什么会发生这种情况,但我希望有人可能比我提出的更好的解决方案。 Simplified illustration of the problem: 问题的简化说明:

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'

I've thought of: 我想到了:

  1. Using a descriptor, but that seems way more complex than is necessary here 使用描述符,但这似乎比这里必要的更复杂
  2. Using a closure inside of a factory for foo , but nested closures are ugly and messy replacements for objects most of the time, imo 在工厂里面使用一个闭包用于foo ,但嵌套的闭包是大多数时候对象的丑陋和凌乱的替换,imo
  3. Wrapping the Foo instance in a method that passes its self down to the Foo instance as instance , basically a decorator, that is what I actually add to Baz , but that seems superfluous and basically just a more complicated way of doing the same thing as (2) Foo实例包装在一个方法中,将其self向下传递给Foo实例作为instance ,基本上是一个装饰器,这就是我实际添加到Baz ,但这似乎是多余的,基本上只是一种更复杂的方式来做同样的事情( 2)

Is there a better way then any of these to try to accomplish what I want, or should I just bite the bullet and use some closure factory type pattern? 有没有比这更好的方法来尝试完成我想要的东西,或者我应该咬紧牙关并使用一些封闭工厂类型模式?

One way to do this is to attach the callable objects to the class as unbound methods. 一种方法是将可调用对象作为未绑定方法附加到类。 The method constructor will work with arbitrary callables (ie instances of classes with a __call__() method)—not just functions. 方法构造函数使用任意callables(即带有__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

In Python 3, there's no such thing as an unbound instance (classes just have regular functions attached) so you might instead make your Foo class a descriptor that returns a bound instance method by giving it a __get__() method. 在Python 3中,没有未绑定的实例(类只附加了常规函数),所以你可以改为使你的Foo类成为一个描述符,通过给它一个__get__()方法来返回一个绑定的实例方法。 (Actually, that approach will work in Python 2.x as well, but the above will perform a little better.) (实际上,这种方法也适用于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

The trouble you're running into is that your object is not being bound as a method of the Baz class you're putting it in. This is because it is not a descriptor, which regular functions are ! 你遇到的麻烦是你的对象没有被绑定为你所使用的Baz类的方法。这是因为它不是描述符, 常规函数是

You can fix this by adding a simple __get__ method to your Foo class that makes it into a method when it's accessed as a descriptor: 您可以通过向Foo类添加一个简单的__get__方法来解决这个问题,该方法在将其作为描述符进行访问时将其作为方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM