我正在编写一个Python类来包装/修饰/增强名为petl的包中的另一个类,这是一个用于ETL(数据移动)工作流的框架。由于设计限制,我不能只是将它子类化;每个方法调用都必须通过我自己的类发送,这样我就可以控制传回哪种对象。所以原则上这是一个代理类,但我在使用现有的答案/食谱时遇到了一些麻烦。这就是我的代码:
from functools import partial
class PetlTable(object):
"""not really how we construct petl tables, but for illustrative purposes"""
def hello(name):
print('Hello, {}!'.format(name)
class DatumTable(object):
def __init__(self, petl_tbl):
self.petl_tbl = petl_tbl
def __getattr__(self, name):
"""this returns a partial referencing the child method"""
petl_attr = getattr(self.petl_tbl, name, None)
if petl_attr and callable(petl_attr):
return partial(self.call_petl_method, func=petl_attr)
raise NotImplementedError('Not implemented')
def call_petl_method(self, func, *args, **kwargs):
func(*args, **kwargs)
然后我尝试实例化一个表并调用一些东西:
# create a petl table
pt = PetlTable()
# wrap it with our own class
dt = DatumTable(pt)
# try to run the petl method
dt.hello('world')
这给出TypeError: call_petl_method() got multiple values for argument 'func'
。
这只发生在位置参数上; kwargs似乎很好。我很确定它与self
没有被传入有关,但我不确定解决方案是什么。谁能想到我做错了什么,或者更好的解决方案呢?
答案 0 :(得分:0)
这似乎是混合位置和关键字args的常见问题: TypeError: got multiple values for argument
为了解决这个问题,我将位置arg func
从call_petl_method
中取出并放入一个不太可能与子函数的kwargs重叠的kwarg中。有点hacky,但它确实有效。
我最后写了一个Proxy
课来做这一切:
class Proxy(object):
def __init__(self, child):
self.child = child
def __getattr__(self, name):
child_attr = getattr(self.child, name)
return partial(self.call_child_method, __child_fn__=child_attr)
@classmethod
def call_child_method(cls, *args, **kwargs):
"""
This calls a method on the child object and wraps the response as an
object of its own class.
Takes a kwarg `__child_fn__` which points to a method on the child
object.
Note: this can't take any positional args or they get clobbered by the
keyword args we're trying to pass to the child. See:
https://stackoverflow.com/questions/21764770/typeerror-got-multiple-values-for-argument
"""
# get child method
fn = kwargs.pop('__child_fn__')
# call the child method
r = fn(*args, **kwargs)
# wrap the response as an object of the same class
r_wrapped = cls(r)
return r_wrapped
答案 1 :(得分:0)
这也将解决问题。它根本不使用partial
。
class PetlTable(object):
"""not really how we construct petl tables, but for illustrative purposes"""
def hello(name):
print('Hello, {}!'.format(name))
class DatumTable(object):
def __init__(self, petl_tbl):
self.petl_tbl = petl_tbl
def __getattr__(self, name):
"""Looks-up named attribute in class of the petl_tbl object."""
petl_attr = self.petl_tbl.__class__.__dict__.get(name, None)
if petl_attr and callable(petl_attr):
return petl_attr
raise NotImplementedError('Not implemented')
if __name__ == '__main__':
# create a petl table
pt = PetlTable()
# wrap it with our own class
dt = DatumTable(pt)
# try to run the petl method
dt.hello('world') # -> Hello, world!