将方法添加到从其他方法生成的类

时间:2013-08-04 22:43:17

标签: python

我有这样的课程:

class Tool(object):
    def do_async(*args):
        pass

我想自动生成使用异步方法的非异步方法:

class Tool(object):
    def do_async(*args):
        pass
    def do(*args):
        result = self.do_async(*args)
        return magical_parser(result)

这变得特别棘手,因为每个方法都需要作为两者一个对象和类方法来访问,这通常是通过这个神奇的装饰器实现的:

class class_or_instance(object):
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, obj, cls):
        if obj is not None:
            f = lambda *args, **kwds: self.fn(obj, *args, **kwds)
        else:
            f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
        functools.update_wrapper(f, self.fn)
        return f

我如何制作这些方法,并确保它们可以作为类和对象方法访问?这似乎可以用装饰器完成,但我不确定如何。

(请注意,我事先并不知道任何方法名称,但我知道所有需要新伙伴的方法在名称的末尾都有_async。)

我认为我已经相当接近了,但是这种方法没有将函数适当地设置为类/对象方法:

def process_asyncs(cls):

    methods = cls.__dict__.keys()
    for k in methods:
        methodname = k.replace("_async","")
        if 'async' in k and methodname not in methods:

            @class_or_instance
            def method(self, verbose=False, *args, **kwargs):
                response = self.__dict__[k](*args,**kwargs)
                result = self._parse_result(response, verbose=verbose)
                return result

            method.__docstr__ = ("Returns a table object.\n" +
                    cls.__dict__[k].__docstr__)

            setattr(cls,methodname,MethodType(method, None, cls))

1 个答案:

答案 0 :(得分:3)

不要从__dict__获得其他方法;使用getattr()代替描述符协议可以启动。

并且不要将method函数包装在MethodType()对象中,因为它会中和您放在method上的描述符。

您需要绑定 k到您生成的函数;封闭的k会因循环而改变:

@class_or_instance
def method(self, verbose=False, _async_method_name=k, *args, **kwargs):
    response = getattr(self, _async_method_name)(*args,**kwargs)
    result = self._parse_result(response, verbose=verbose)
    return result

cls.__dict__[methodname] = method

不要忘记最后返回cls;我已将此更改为使用单独的函数创建新范围以提供新的本地名称_async_method_name而不是关键字参数;这避免了*args和显式关键字参数的困难:

def process_asyncs(cls):

    def create_method(async_method):

        @class_or_instance
        def newmethod(self, *args, **kwargs):
            if 'verbose' in kwargs:
                verbose = kwargs.pop('verbose')
            else:
                verbose = False
            response = async_method(*args,**kwargs)
            result = self._parse_result(response, verbose=verbose)
            return result
        return newmethod

    methods = cls.__dict__.keys()
    for k in methods:
        methodname = k.replace("_async","")
        if 'async' in k and methodname not in methods:
            async_method = getattr(cls, k)
            setattr(cls, methodname, create_method(async_method))

    return cls