我有这样的课程:
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))
答案 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