Какое простейшее определение класса пересылки в 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) делает то же самое, что и прямолинейная реализация

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

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

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

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

На самом деле не волнует:

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

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

 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; ie 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 вы не сможете получить доступ к элементам, начинающимся с __ таким образом.