2017-01-15 4 views
0

Я пишу класс 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

Было бы полезно, чтобы точно знать, что 'petl' это, а также то, что' petl.fromcsv («test.csv») 'возвращает-ни определяется в коде, показанном в вашем вопросе. – martineau

+0

есть [этот] (http://stackoverflow.com/questions/9942536/how-to-fake-proxy-a-class-in-python) вопрос, который содержит ссылку на рецепт activestate. Может быть полезной альтернативой. –

+0

@martineau Спасибо за отзыв. Я обновил вопрос с дополнительной информацией о том, что такое библиотека и что она возвращает, однако я просто хотел указать, что это не совсем специфично для 'petl'. Это просто переход к другому классу, который может быть чем угодно. – Rob

ответ

0

Это, кажется, общая проблема с перемешивающими позиционными и ключевыми словом аргументов: TypeError: got multiple values for argument

Чтобы обойти это, я взял позиционное ARG func из call_petl_method и поместить его в 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!