一些背景: 我们有一个交易系统,我们根据交易账单所在的国家/地区划分流量。我们有一个记录表,存在于2个实例中,一个DB记录到EU的事务,另一个记录到其他任何地方。我们还有一个测试库,用于管理和隐藏与DB一起工作的内容,粗略地说每个表由一个类表示。我有一个表示表的类,db会话管理器类为该类的两个实例中的每一个都有两个成员。我想要做的是创建一个通用的'meta dao'类,它将对它进行任意调用,检查args,并根据其中一个输入参数,随后将调用分派给正确的db instance-representation类实例。我最初想过只是重载每一种方法,但这很笨拙。
我正在考虑使用__getattr__
覆盖方法查找,以便我可以根据方法__getattr__
收到的名称调用正确的实例,但据我所知,我不能inspect
来自__getattr__
内的传入方法参数,因此在这种情况下我无法正确地从其中发送。有没有人对我可以追求的不同方向有任何想法,或者从__getattr__
内“检查”参数而不仅仅是方法名称?
[edit]这是我所说的通用版本:
class BarBase(object):
def __init__(self, x):
self.x = x
def do_bar(self, i):
return self.x * i
class FooBar(BarBase):
def __init__(self, x):
super(FooBar, self).__init__(x)
def do_foo(self, i):
return self.x + i
class MetaFoo(object):
def __init__(self, bar_manager):
self.foo_manager = bar_manager
#something here that will take an arbitrary methodname and args as
#long as args includes a value named i, inspect i, and call
#bar_manager.fooa.[methodname](args) if i < 10,
#and bar_manager.foob.[methodname](args) if i >= 10
class BarManager(object):
def __init__(self):
self.bar_list = {}
def __get_fooa(self):
if 'fooa' not in self.bar_list.keys():
self.bar_list['fooa'] = FooBar('a')
return self.bar_list['fooa']
fooa = property(__get_fooa)
def __get_foob(self):
if 'foob' not in self.bar_list.keys():
self.bar_list['foob'] = FooBar('b')
return self.bar_list['foob']
foob = property(__get_foob)
def __get_foo(self):
if 'foo' not in self.bar_list.keys():
self.bar_list['foo'] = MetaFoo(self)
return self.bar_list['foo']
答案 0 :(得分:2)
这些方面的某些事情应该有效:
class ProxyCall(object):
'''Class implementing the dispatch for a certain method call'''
def __init__(self, proxy, methodname):
self.proxy = proxy
self.methodname = methodname
def __call__(self, *p, **kw):
if p[0] == "EU": # or however you determine the destination
return getattr(self.proxy.EU, self.methodname)(*p, **kw);
else:
return getattr(self.proxy.OTHER, self.methodname)(*p, **kw);
class Proxy(object):
'''Class managing the different "equivalent" instances'''
def __init__(self, EU, OTHER):
self.EU = EU
self.OTHER = OTHER
def __getattr__(self, name):
if not hasattr(self.EU, name):
# no such method
raise AttributeError()
else:
# return object that supports __call__ and will make the dispatch
return ProxyCall(self, name)
然后,您将创建两个实例并将它们组合在一个代理对象中:
eu = make_instance(...)
other = make_instance(...)
p = Proxy(eu, other)
p.somemethod(foo)
答案 1 :(得分:2)
python decorators是你的朋友。你可以做这样的事情
class MetaFoo(object):
def overload(func):
"""
we need to check a named variable so for simplicity just checking kwargs
"""
def _wrapper(*args, **kwargs):
if kwargs.get('i',0) < 10:
# get func.func_name from foo and call it
print "calling foo.",func.func_name
else:
print "calling bar.",func.func_name
func(*args, **kwargs)
return _wrapper
@overload
def func1(self, i):
print "default functionality"
MetaFoo().func1(i=5)
MetaFoo().func1(i=10)
输出:
calling foo. func1
default functionality
calling bar. func1
default functionality
如果您没有多少方法可以覆盖,您可以单独应用装饰器,甚至可以传递参数,例如diff阈值到不同的方法,但是如果想要覆盖所有方法,你可以添加一个重载给定类的所有方法的元类,但在这种情况下,按__getattr__
的建议覆盖sth
是一个很好的选择
答案 2 :(得分:0)
基于传递的参数进行调度分为两步:
__getattr__
返回代理方法以下是一个例子:
from functools import partial
class TwoFold(object):
EU = ('GERMANY','FRANCE','ITALY','GREECE',)
def __getattr__(self, name):
try:
EU = object.__getattribute__(self, 'EU_' + name)
Other = object.__getattribute__(self, 'Other_' + name)
except AttributeError:
raise AttributeError(
"%r is missing an EU_%s or Other_%s" % (self, name, name)
)
judge = partial(self._judge, name, EU, Other)
return judge
def _judge(self, method_name, EU, Other, *args, **kwargs):
if kwargs['country'].upper() in self.EU:
method = EU
else:
method = Other
return method(*args, **kwargs)
def EU_log(self, tax, country):
print "logging EU access for %s, tax rate of %r" % (country, tax)
def Other_log(self, tax, country):
print "logging non-EU access for %s, tax rate of %r" % (country, tax)
if __name__ == '__main__':
test = TwoFold()
test.log(7.5, country='France')
test.log(10.1, country='Greece')
test.log(8.9, country='Brazil')
test.howsat('blah')
运行时,这会给出:
logging EU access for France, tax rate of 7.5
logging EU access for Greece, tax rate of 10.1
logging non-EU access for Brazil, tax rate of 8.9
其次是:
Traceback (most recent call last):
File "test.py", line 29, in <module>
test.howsat('blah')
File "test.py", line 10, in __getattr__
raise AttributeError("%r is missing an EU_%s or Other_%s" % (self, name, name))
AttributeError: <__main__.TwoFold object at 0x00B4A970> is missing an
EU_howsat or Other_howsat
要完成这项工作,您必须始终使用相同的关键字参数(并在调用函数时将其命名)或始终使参数位于相同位置。或者,您可以为每种样式/类别/任何类型的方法创建几个不同的代理。