2015-08-02 2 views
4

Я хотел бы реализовать класс пересылки в Python, реализуя некоторый базовый класс, который принимает в своем конструкторе другую реализацию одного и того же базового класса и пересылает этот класс для каждого метода в базовом классе.Какое простейшее определение класса пересылки в Python?

Это может быть полезно для предоставления точки настройки, добавления кеширования для определенных методов, ведения журнала по каждому сайту вызова и тому подобного.

Это просто написать это вручную. С учетом этого базового класса:

class BaseClass(object): 
    def method_a(self, *args): 
     pass 

    @property 
    def prop_b(self): 
     pass 

    def method_c(self, a, b, **kwargs): 
     pass 

Вот простая реализация пересылки.

class ForwardBaseClass(BaseClass): 
    def __init__(self, underlying_implementation): 
     self._impl = underlying_implementation 

    def method_a(self, *args): 
     return self._impl.method_a(*args) 

    @property 
    def prop_b(self): 
     return self._impl.prop_b 

    def method_c(self, a, b, **kwargs): 
     return self._impl.method_c(a, b, **kwargs) 

Обратной просто писать код, что есть много шаблонного кода, и что код должен быть написан для каждого метода. Поскольку методы добавляются в базовый класс, аналогичный код должен быть добавлен в класс пересылки; и если изменения желательны в том, как класс выполняет свою переадресацию, они должны быть распределены по всем методам. (Предположим, например, что мы хотели изменить класс, чтобы он использовал менеджер контекста, который он использовал для каждого вызова.)

Есть ли короткий, Pythonic способ сделать это, либо на базовом языке, с встроенных модулей или с некоторым надежным модулем на PyPI?

Желаемых характеристики:

  • Точных сигнатуры методов
    • Перенаправленная подпись должна соответствовать базовому классу
    • Нет *args, **kwargs на каждую подписи
  • Методы с @property декоратором обернуты надлежащим образом
  • __<method>__ методы, если они существуют, завернутые правильно
  • Отсутствия дополнительных затрат на вызов метода по прямым вперед реализациям
  • dir(instance) делает то же самое, как прямой вперед реализация

Приятно иметь:

  • сообщения об ошибках, когда базовый класс не удовлетворяет некоторым необходимым критериям
  • Значимые
  • строки документации
  • Возможности вывести из полученного объекта
  • Методы с @staticmethod декоратором направляется к классу переданным в реализации
  • Методы с @classmethod декоратором пересылаемых к классу пройденного в реализации
  • __init__ и __new__ не пересылаются

Места, где есть вероятность дополнительная сложность:

  • Базовый класс имеет метакласса
  • Базовый класс имеет несколько базовых классов своей собственной
  • Некоторые из более эзотерических __<method>__ с и их семантика

Не волнует:

  • Несколько базовых классов
  • Декораторы декоративных декоративных элементов, отличные от , @staticmethod, и @classmethod
  • Изменение в базовый класс после создания класса экспедиторской
+0

вы можете использовать 'getattr' для пересылки всего, кроме' __init__' и '__new__' (и других вещей, которые вы не хотите) для' self._impl'. это может быть не все ваши желаемые свойства, хотя ... –

+0

Я сомневаюсь, что есть короткий, Pythonic или иначе способ сделать это, честно говоря! – jonrsharpe

+0

В частности, «Никакая дополнительная стоимость за вызов метода за прямолинейную реализацию», похоже, трудно удовлетворить, по крайней мере, если вы хотите поймать всевозможные странные ситуации. Решение, которое вы предлагаете, например, перенаправляет вызовы на объект 'self._impl' - этот дополнительный вызов несет дополнительную плату за вызов. Также требование для 'dir (instance)' to work означает еще одну работу, которая может повредить стоимость вызова. – skyking

ответ

0

просто уточнить мой комментарий: что я имею в виду +/- это (нужно больше работать, хотя ... и снова: не может удовлетворить все ваши требования):

class BaseClass(object): 
    def method_a(self, *args): 
     pass 

    @property 
    def prop_b(self): 
     pass 

    def method_c(self, a, b, **kwargs): 
     pass 

class ForwardBaseClass(BaseClass): 
    def __init__(self, underlying_implementation): 
     self._impl = underlying_implementation 

    def __getattr__(self, name): 
     # here you may have to exclude thigs; i.e. forward them to 
     # self.name instead for self._impl.name 
     try: 
      return getattr(self._impl, name) 
     except AttributeError: 
      # do something else... 

bc = BaseClass() 
fbc = ForwardBaseClass(bc) 
print(fbc.prop_b) 
print(fbc.method_c(1,2,c=5)) 
print(dir(fbc)) 

обратите внимание, что из-за python name mangling вы не сможете получить доступ пользователей, начиная с __ таким образом.

+0

Это не будет работать для большинства методов '__special__', так как они просматриваются в слотах и ​​никогда не пропускаются через' __getattr__'. Вы также можете реализовать '__dir__', чтобы отображать атрибуты обернутого объекта. – Blckknght

+0

Я согласен (и уже упомянул один из ваших вопросов). но до тех пор, пока не будет никакой реакции с OP, я не пойду дальше этой идеей ... –

+0

Я думаю, что мой предыдущий комментарий вышел более суровым, чем я предполагал. Подход, который вы показываете, действительно лучший в целом, критерии опроса довольно глупы. – Blckknght

Смежные вопросы