Python 将方法添加到由其他方法生成的类中

Python 将方法添加到由其他方法生成的类中,python,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 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
如何创建这些方法,并确保它们可以作为类和对象方法访问?这似乎是装饰师可以做的事情,但我不确定如何做

(请注意,我事先不知道任何方法名称,但我知道所有需要新伙伴的方法在名称末尾都有
\u 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))

不要从
目录中获取其他方法;改用
getattr()
,这样描述符协议就可以生效了

并且不要将
方法
函数包装在
MethodType()
对象中,因为这样会抵消您在
方法
上放置的描述符

您需要将
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
;我改变了这一点,使用一个单独的函数来创建一个新的作用域,以提供一个新的本地名称
\u async\u method\u 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

不要从
目录中获取其他方法;改用
getattr()
,这样描述符协议就可以生效了

并且不要将
方法
函数包装在
MethodType()
对象中,因为这样会抵消您在
方法
上放置的描述符

您需要将
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
;我改变了这一点,使用一个单独的函数来创建一个新的作用域,以提供一个新的本地名称
\u async\u method\u 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

我试过了,但出现了错误:
TypeError:“dictproxy”对象不支持项分配
。我的类有
\uuuu元类=abc.ABCMeta
;我真的不知道这是否有必要或与此相关。您在问题中发布的解决方案将受到以下事实的影响:
async\u方法
是一个循环中的闭包;所有生成的方法都将指向一个异步方法。有关代码中的
async\u方法
名称的错误,以及我为什么将
k
绑定到解决方案中的关键字参数,请参阅,以获得更全面的解释。是的,通过创建新范围;使用一个单独的函数,在调用时返回
newmethod()
。请参阅我先前评论中的链接问题。是的,这是我最初的答案,但您的编辑缓存了该方法。:-)后期查找也允许您查找实例属性,是的。我已尝试过,但出现错误:
TypeError:“dictproxy”对象不支持项分配
。我的类有
\uuuu元类=abc.ABCMeta
;我真的不知道这是否有必要或与此相关。您在问题中发布的解决方案将受到以下事实的影响:
async\u方法
是一个循环中的闭包;所有生成的方法都将指向一个异步方法。有关代码中的
async\u方法
名称的错误,以及我为什么将
k
绑定到解决方案中的关键字参数,请参阅,以获得更全面的解释。是的,通过创建新范围;使用一个单独的函数,在调用时返回
newmethod()
。请参阅我先前评论中的链接问题。是的,这是我最初的答案,但您的编辑缓存了该方法。:-)后期查找也允许您查找实例属性,是的。作为实例或类方法调用这些方法的语义是什么?当被称为实例方法时,他们是否会将实例用于任何事情?这听起来像是模块级函数的工作,或者如果您真的非常确定这些东西需要是方法,那么可能是
staticmethod
。是的,通常这些只是模块级函数,但在某些情况下需要实例。基本上,这是针对一套web查询工具的,通常您可以只使用classmethod,但有时您需要先使用对象登录到服务。也许有更好的方法,但这种方法迄今为止效果良好。(正在进行的工作:astroquery.readthedocs.org)作为实例或类方法调用这些方法的语义是什么?当被称为实例方法时,他们是否会将实例用于任何事情?这听起来像是模块级函数的工作,或者如果您真的非常确定这些东西需要是方法,那么可能是
staticmethod
。是的,通常这些只是模块级函数,但在某些情况下需要实例。基本上,这是针对一套web查询工具的,通常您可以只使用classmethod,但有时您需要先使用对象登录到服务。也许有更好的方法,但这种方法迄今为止效果良好。(正在进行的工作:astroquery.readthedocs.org)