代理类不能调用child上的方法

时间:2017-01-15 23:28:11

标签: python class proxy

我正在编写一个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没有被传入有关,但我不确定解决方案是什么。谁能想到我做错了什么,或者更好的解决方案呢?

2 个答案:

答案 0 :(得分:0)

这似乎是混合位置和关键字args的常见问题: TypeError: got multiple values for argument

为了解决这个问题,我将位置arg funccall_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!