I have a class with about 200 stub functions, they look something like this:
def name1(self, **kwargs):
return self.run('name-1', kwargs)
def name2(self, ** kwargs):
return self.run('name-2', kwargs)
So on and so forth.
There is a run function that takes care of the real work.
I want to reduce the 200 stub function to just 1 dynamic function that gets called when you make a call to the class with a method name that doesn't exist.
How do I do this?
答案 0 :(得分:4)
您可以在类中使用__getattr__
方法返回run
周围的包装函数。您只需要在方法名称(例如name1
)和应传递给run
方法的字符串(例如"name-1"
)之间进行转换的方法。如果没有任何改变,这当然是最简单的。这是一个使用字典进行翻译的快速实现。如果您需要它来处理许多名称,您可能希望使用字符串操作和正则表达式来完成它。
name_translation_dict = {"name1": "name-1", "name2": "name-2"}
def __getattr__(self, name):
if name not in self.name_translation_dict:
raise AttributeError()
translated_name = self.name_translation_dict[name]
def method(**kwargs):
return self.run(translated_name, kwargs)
return method
答案 1 :(得分:1)
这是@ Blckknight优秀且相对简洁answer的更为通用且可能更有效的版本。
它更长,因为它更通用,因为它使用正则表达式将调用存根函数的名称转换为name
参数以传递类的run()
方法,并且可能是整体效率更高,因为它可以保存它在运行中创建的存根函数,有效地缓存它们,这样如果再次使用它们就不需要重新创建它们。那是因为只有在通常的地方找不到属性时才会调用__getattr__()
,但是在第一次之后它会被调用(在类的__dict__
中)。
import re
class MyClass(object):
_STUB_PREFIX = 'name'
_STUB_PATTERN = _STUB_PREFIX + r'''(\d+)'''
def run(self, name, **kwargs):
print('run({!r}, {})'.format(name, kwargs))
def __getattr__(self, name):
matches = re.search(self._STUB_PATTERN, name)
if not matches:
raise AttributeError(
"'{}' object has no attribute '{}'".format(
self.__class__.__name__, name))
num = matches.group(1)
translated_name = '-'.join((self._STUB_PREFIX, num))
def stub(**kwargs):
return self.run(translated_name, **kwargs)
setattr(type(self), name, stub) # add stub to class
return stub
obj = MyClass()
obj.name1() # -> run('name-1', {})
obj.name2() # -> run('name-2', {})
obj.name1() # -> run('name-1', {}), but __getattr__(self, 'name1') not called
obj.foo42() # -> AttributeError: 'MyClass' object has no attribute 'foo42'