2017-01-15 5 views
0

私はETL(データ移動)ワークフローのフレームワークであるpetlというパッケージから別のクラスをラップ/デコレート/エンハンスするPythonクラスを作成しています。設計上の制約のため、私はそれをサブクラス化することはできません。すべてのメソッド呼び出しは自分のクラスを介して送られなければならないので、どのようなオブジェクトが返されるかを制御することができます。原則としてこれはプロキシクラスですが、既存の回答/レシピを使用していくつかの問題を抱えています。これは私のコードは次のようになります。プロキシクラスが子のメソッドを呼び出せません

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) 

その後、私はテーブルをインスタンス化し、何かを呼び出すようにしてみてください。

​​

これはTypeError: call_petl_method() got multiple values for argument 'func'を与えます。

これは、位置指定の引数でのみ発生します。 kwargsはうまくいくようです。私はかなりそれがselfと渡されていないとしなければならないと確信していますが、私は解決策が何であるか分かりません。誰かが私が間違ってやっていること、あるいはより良い解決策を考えることができますか?

+0

はpetl'があるだけでなく、どのような 'petl.fromcsv(「のtest.CSV」)'リターンが、どちらもあなたの質問に示すコードで定義されていない、まさに '知るために役立つだろう。 – martineau

+0

活性状態のレシピへのリンクが含まれている[this](http://stackoverflow.com/questions/9942536/how-to-fake-proxy-a-class-in-python)の質問があります。有用な選択肢かもしれない。 –

+0

@martineauフィードバックに感謝します。私は、ライブラリが何であるか、それが何を返すかについてのさらなる情報で質問を更新しましたが、これは本当に 'petl'に特有ではないことを指摘したかっただけです。他のクラスへのパススルーに過ぎません。 – Rob

答えて

0

これは、位置とキーワード引数混合に共通の問題であると思われる。それを回避するために TypeError: got multiple values for argument

を、私はcall_petl_methodの外に位置引数funcを取り、と重複する可能性は低いですkwargに入れます子関数のkwargs。少しハッキーですが、それは動作します。

私は一般的に、すべてこれを行うには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 
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! 
関連する問題