2015-11-07 5 views
0

У меня есть классы BasicEvidenceTarget и SchedulableSoma. Иногда я наследую от SchedulableSoma, BasicEvidenceTarget, иногда я наследую только от SchedulableSoma. Когда я наследую от SchedulableSoma, BasicEvidenceTarget, я хочу, чтобы SchedulableSoma переопределил метод BasicEvidenceTarget.inject_basic_evidence. Какой хороший способ сделать это?Как переопределить методы базового класса в Python условно, существуют ли они?

Процентовка выглядит следующим образом:

class SchedulableSoma(SchedulableCluster, Soma): 
    # This is a possible overload of this method in BasicEvidenceTarget. 
    def inject_basic_evidence(self, *args, **kwargs): 
     super().inject_basic_evidence(*args, **kwargs) 
     self.ask_for_reschedule() 

Сейчас я безусловно переопределение метода базового класса, и поэтому, если он не существует, есть немного загрязнения метода: если переопределение называется , вызов super завершится неудачно. Было бы лучше условно генерировать переопределение.

Я чувствую, что есть магия __prepare_subclass__, которая может работать, но я точно не знаю, как это сделать.

+0

Похоже, вам может быть лучше создать отдельный класс «Both» для комбо и переопределить метод в этом классе. Затем либо наследуйте от «Both», либо «SchedulableSoma». – BrenBarn

+0

@BrenBarn: Проблема в том, что существуют три класса, такие как 'BasicEvidenceTarget' - каждый из которых имеет один метод, который' SchedulableSoma' хочет переопределить. Я не хочу создавать 8 классов. –

ответ

3

Вы можете использовать фабричную функцию для создания соответствующего класса в то время, когда вы определяете класс, который наследует его. Это просто эскиз:

def makeSubclass(otherclass): 
    class MixedClass(otherclass): 
     if otherClass == BasicEvidenceTarget: 
      def inject_basic_evidence(...): 
       # ... 
     elif otherClass == WhateverOtherClass: 
      def some_other_method(...): 
       # ... 

Тогда вы могли бы сделать:

class SchedulableSoma(makeSubclass(BasicEvidenceTarget)): 
    # ... 

Другая возможность состоит в том, чтобы просто определить все методы SchedulableSoma, но включают в себя проверку в них, которые вызывают исключения, если текущий экземпляр делает не наследуется от соответствующего класса. Что-то вроде:

class SchedulableSoma(object):    
    def inject_basic_evidence(self, *args, **kwargs): 
     if not isinstance(self, BasicEvidenceTarget): 
      raise TypeError("Cannot call inject_basic_evidence unless you inherit from BasicEvidenceTarget") 

    def some_other_method(self, *args, **kwargs): 
     if not isinstance(self, SomeOtherClass): 
      raise TypeError("Cannot call some_other_method unless you inherit from SomeOtherClass") 

    # similar checks for other classes 

Таким образом, вызов inject_basic_evidence не сможет сразу же с более конкретным сообщением об ошибке, а не провал на super вызова с более неясным сообщением о «супер-объекте не имеет атрибута» и т.п. ,

В конечном итоге вам может понадобиться подумать о том, существует ли более надежный способ структурирования иерархии классов. Магическим является то, что классы меняют свое поведение в зависимости от того, появляются ли в иерархии наследования некоторые другие классы, и это может запутать пользователей или привести к непредвиденным взаимодействиям между классами в будущем.

+0

О, это умно ... –

+0

Хорошее решение. Но я думаю, что ваш совет по реструктуризации - лучшая часть ответа. Это трудно понять, но это кажется хорошим примером для композиции над наследованием. – RobertB

-1

Я думаю, что эта проблема может быть решена с помощью другой схемы наследования. Используя свою идею:

class YourClass(SchedulableSoma, BasicEvidenceTarget): 
    def inject_basic_evidence(self, *args, **kwargs): 
     if isinstance(self, BasicEvidenceTarget): 
      BasicEvidenceTarget.inject_basic_evidence(self, *args, **kwargs) 
     else: 
      super().inject_basic_evidence(*args, **kwargs) 
     self.ask_for_reschedule() 

Или

class YourClass(SchedulableSoma, BasicEvidenceTarget): 
    if issubclass(YourClass, BasicEvidenceTarget): 
     def inject_basic_evidence(self, *args, **kwargs): 
      super().inject_basic_evidence(*args, **kwargs) 
      self.ask_for_reschedule() 
+0

Но я хочу, чтобы метод вообще не существовал, если он не является экземпляром этого базового класса ... –

+0

Маска isubclass до определения функции может сделать трюк. –

+0

это не работает, к сожалению :) –

1

То, что вы пытаетесь сделать, это невозможно во время определения класса без funcy метаклассом бизнеса. Это может быть сделано во время создания экземпляра с помощью следующего кода:

class SchedulableSoma(SchedulableCluster, Soma): 
    def __init__(self,*args,**kwargs): 
     super().__init__(self,*args,**kwargs) 
     if hasattr(self,"inject_basic_evidence"): 
      def inject_basic_evidence(*args, **kwargs): 
       super().inject_basic_evidence(*args, **kwargs) 
       self.ask_for_reschedule() 
      self.inject_basic_evidence = inject_basic_evidence 

Примечание: Это не работает, если подкласс SchedulableSoma переопределениях inject_basic_evidence

+0

Это решение не будет работать, если дочерний класс также переопределяет этот метод, верно? –

+0

Вы правы. Нет, не будет. Вы на самом деле пытаетесь это сделать? – ppperry