【问题标题】:Proxy class can't call methods on child代理类不能调用孩子的方法
【发布时间】:2017-05-30 18:25:29
【问题描述】:

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

【问题讨论】:

  • 确切知道petl 是什么以及petl.fromcsv('test.csv') 返回什么会很有帮助——您的问题中显示的代码中都没有定义。
  • 有一个 this 问题,其中包含指向 activestate 配方的链接。可能是一个有用的替代方案。
  • @martineau 感谢您的反馈。我更新了这个问题,提供了更多关于库是什么以及它返回什么的信息,但我只是想指出这并不是petl 所特有的。它只是传递到另一个类,可以是任何东西。
  • 那我建议你edit你的问题并添加另一个类和一些演示问题的代码(使用它)。

标签: python class proxy


【解决方案1】:

这也将解决问题。它根本不使用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!

【讨论】:

    【解决方案2】:

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

    为了解决这个问题,我从call_petl_method 中取出位置参数func 并将其放入一个不太可能与子函数的kwarg 重叠的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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-10
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      • 2013-04-19
      • 1970-01-01
      • 2015-03-14
      • 2017-07-19
      相关资源
      最近更新 更多