В одном из моих проектов мне также нужно было сделать одну конкретную вещь, а именно, что даже базовый объект должен фактически выполнять метод, который был переопределен в декораторе. Это на самом деле довольно легко сделать, если вы знаете, куда нацеливаться.
Вариант использования:
- У меня есть объект X с методами A и B.
- Я создаю декоратор класса Y, который переопределяет A.
- Если я создам экземпляр Y (X) и вызову A, он будет использовать декорированный A, как и ожидалось.
- Если B вызывает A, то если я создаю экземпляр Y (X) и вызываю B на декораторе, то вызов изнутри B переходит к старому A на исходном объекте, что было нежелательно. Я хочу, чтобы старая Б тоже назвала новую А.
Можно достичь такого поведения следующим образом:
import inspect
import six # for handling 2-3 compatibility
class MyBaseDecorator(object):
def __init__(self, decorated):
self.decorated = decorated
def __getattr__(self, attr):
value = getattr(self.decorated, attr)
if inspect.ismethod(value):
function = six.get_method_function(value)
value = function.__get__(self, type(self))
return value
class SomeObject(object):
def a(self):
pass
def b(self):
pass
class MyDecorator(MyBaseDecorator):
def a(self):
pass
decorated = MyDecorator(SomeObject())
Это может не сработать из коробки, так как я набрал все остальное, кроме метода getattr, из головы.
Код ищет запрошенный атрибут в декорированном объекте, и если это метод (сейчас не работает со свойствами, но изменение для их поддержки не должно быть слишком сложным), тогда код извлекает реальную функцию вне метода и используя вызов интерфейса дескриптора, он «перепривязывает» функцию как метод, но в декораторе. Затем он возвращается и, скорее всего, выполняется.
Эффект этого состоит в том, что если b
когда-либо вызывает a
на исходном объекте, то когда у вас оформлен объект и есть какой-либо вызов метода, исходящий от декоратора, декоратор гарантирует, что все методы, к которым получен доступ, вместо этого он привязан к декоратору, поэтому ищет вещи, используя декоратор, а не исходный объект, поэтому методы, указанные в декораторе, имеют приоритет.
П.С .: Да, я знаю, это выглядит как наследование, но это сделано в смысле объединения нескольких объектов.