#python #oop #inheritance #overriding #decorator
Вопрос:
Я пытаюсь написать декоратор, который можно использовать для переопределения метода во время наследования, но внешне. Что-то вроде:
class A:
def some_method(self):
print('does stuff')
def override(method):
# override decorator
....
def some_method_override():
print('does other stuff')
@override(method, some_method_override)
class B:
#overridden
a = A()
b = B()
a.some_method() # prints 'does stuff'
b.some_method() # prints 'does other stuff'
Если бы кто-нибудь мог поделиться некоторыми идеями по этому поводу, это было бы здорово!
Комментарии:
1. Почему? Это кажется довольно странной вещью, без реальной пользы и нескольких странных предостережений (например, нарушение одного аргумента
super
).2. @user2357112supportsMonica потенциально может иметь множество классов, наследуемых от A, и переопределять определенный метод таким же образом, но изменять другие, чтобы вы могли повторно использовать методы
3. Я бы заглянул в макет библиотеки, если вы пытаетесь динамически переопределять методы. Вот ресурс — docs.python.org/3/library/unittest.mock.html
4. (упс, означало нарушение супер с 0 аргументами в моем предыдущем комментарии-супер с одним аргументом — это странный исторический артефакт, который почти никогда ни для чего не пригодится)
Ответ №1:
Вот как я бы решил эту проблему на основе прецедента;
- ДЛЯ ТЕСТОВ — я бы заглянул в библиотеку макетов, если вы пытаетесь динамически переопределять методы. Вот ресурс — https://docs.python.org/3/library/unittest.mock.html
- ДЛЯ ОТДЕЛЬНОГО КЛАССА — если это изменение для всего класса, я бы выбрал дочерний класс, который переопределяет исходный метод; это сделает ваш код намного более явным
class A: def some_method(self): print('do_stuff_a') class B(A): def some_method(self): print('do_stuff_b') a = A() a.some_method() # print 'do_stuff_a' b = B() a.some_method() # prints 'do_stuff_b'
- ДЛЯ КОНКРЕТНОЙ СИТУАЦИИ — если вы хотите, чтобы такое поведение происходило только иногда, я бы использовал шаблон кода наблюдателя состояния- вот пример
class A: def __init__(self) self._use_method_a = True def use_method_a(self, use_method_a): self._use_method_a = use_method_a def some_method(self): if self._use_method_a: return self._method_a() else: return self._method_b() def _method_a(self): print('do_stuff_a') def _method_b(self): print('do_other_stuff_b') a = A() a.some_method() # print 'do_stuff_a' a.use_method_a(False) a.some_method() # prints 'do_other_stuff_b'
Ответ №2:
Как упоминали другие в комментариях, я не вижу здесь никакой необходимости в декораторе. Это просто вопрос присвоения атрибутов класса.
Наследование не происходит, когда определен класс; методы из родительского класса не «втягиваются» в дочерний класс. Скорее всего, это происходит во время поиска атрибутов. Если B.some_method
не определено, то A.some_method
используется значение. Поэтому, чтобы переопределить метод родительского класса, вы просто предоставляете определение нужного атрибута класса. Методы-это просто атрибуты класса со значениями типа function
.
class A:
def some_method(self):
print('does stuff')
def some_method_override(self):
print('does other stuff')
class B(A):
some_method = some_method_override
a = A()
b = B()
a.some_method() # prints 'does stuff'
b.some_method() # prints 'does other stuff'
Если бы вам действительно нужен был декоратор, это было бы просто что-то вроде
def override(old: str, new: Callable):
def _(cls):
setattr(cls, old, new)
return cls
return _
def some_method_override(self):
print('does other stuff')
class A:
def some_method(self):
print('does stuff')
@override('some_method', some_method_override)
class B(A):
pass