2013-04-18 3 views
1

Не мог придумать лучшего заголовка вопроса. Не стесняйтесь редактировать. У меня есть базовый класс, который унаследован многими классами (что, в свою очередь, может иметь больше подклассов). Для каждого класса у меня есть последовательность операций, которые мне нужно выполнить после инициализации. Последовательность инкапсулируется в функции runme(), которая выполняет ряд метода объекта вызываетЧастично инициализирующий базовый класс

class myBase(object): 
    def __init__(self,neg,op,value): 
     self.neg = neg 
     self.op = op 
     self.value = value 
     #Process 
     self.runme() 

    def runme(self): 
     self.preprocess() 
     self.evaluate() 
     self.postprocess() 

    def preprocess(self): 
     pass 

    def evaluate(self): 
     pass 

    def postprocess(self): 
     pass 

Подклассы должны принимать одни и те же атрибуты, что и основание (и любые дополнительные атрибуты). Все они будут чрезмерно ездить три функции - preprocess, evaluate и postprocess

class childA(myBase): 
    def __init__(self,neg,op,value,ad1): 
     super(childA,self).__init__(neg,op,value) 
     self.ad1 = ad1 
     #Must call runme() here again?? 
     runme() 

    def evaluate(): 
     #Something using self.ad1 
     blah = self.ad1+self.value 

Как я понимаю, это создает проблему - childA вызывает базу __init__ первый, который вызывает runme(), который, в свою очередь, будет вызывать evaluate. Поскольку ребенок чрезмерно едет evaluate, определение ребенка в evaluate выполняется, но self.ad1 не имеет еще был создан экземпляр, это бросает AttributeError

можно удалить self.runme() из MyBase и проблема, скорее всего, исчезнет, ​​но я может дополнительно sublcass childA в childAA

class childAA(childA): 
    def __init__(self,neg,op,value,ad1): 
     super(childAA,self).__init__(neg,op,value,ad1) 
     self.runme() 

И проблема может проявляться снова и снова. Я не могу удалить runme() из childâ-х __init__, потому что могут быть сформированы объекты как childA и childAA (и будут нуждаться в обработке)

В настоящее время в качестве обходного пути, я не называю runme() в __init__, вместо того, чтобы вызвать его из вызывающего после инициализации.

obja=childA(foo,bar,baz,ad1) 
obja.runme() 

Более простой альтернативой является вызов super() в конце ребенка __init__, но это не появляются быть правым

Другой способ - Скажите базовый класс отложить вызывания Runme() к дочернему классу. Это возможно? Скажем в myBase, я делаю

def __init__(self,neg,op,value): 
     self.neg = neg 
     self.op = op 
     self.value = value 
     #Process 
     if some_condition which checks if this is being called by a derived class: 
      self.runme() 

Какой, если это лучший способ его решить? Альтернативно, это общая проблема и какие другие предлагаемые решения?

EDIT

Два ответа были опубликованы (и удален), который согласился лучшим способ, кажется, чтобы оставить runme() вызов в базовом классе, а затем вызвать super() на конце от ребенка __init__

class myBase(object): 
    def __init__(self,neg,op,value): 
     self.neg = neg 
     self.op = op 
     self.value = value 
     #Process 
     self.runme() 

class childA(myBase): 
     def __init__(self,neg,op,value,ad1): 
      self.ad1 = ad1 
      super(childA,self).__init__(neg,op,value) 

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

class childA(myBase): 
    def __init__(self,neg,op,value,ad1): 
     self.ad1 = ad1 
     self.internal_value = self.value #Not yet initialized!! 
     super(childA,self).__init__(neg,op,value) 

этот код можно поместить в какой-либо другой функции preprocess() или что вызывается первым в runme()

def preprocess(self): 
    self.internal_value = value 
    #Rest of the stuff 
+1

посмотреть http://rhettinger.wordpress.com/2011/05/26/super-considered-super/; Я думаю, что вообще нелогично называть 'super()' в конце, если '__init__' делегировать завершение инициализации класса. – thkang

+0

Почему вы думаете, «что не может быть правильным»? Это именно то, что вам нужно в этом случае, и это самое простое решение. Я не вижу причин использовать очень сложный код только потому, что кто-то сказал вам, что решение _easy_ не кажется правильным. –

+0

@ running.t Да, вы правы. На самом деле мое первоначальное решение не предусматривало удаления вызова 'runme()' от детей. Изменил мой вопрос, чтобы включить то, что я делаю сейчас, потому что два человека отправили один и тот же ответ, а затем удалили его – RedBaron

ответ

0

Если детские __init__ требуют частично инициализируются объекты, чтобы продолжить, вызывая super() в конце не будет работать в самом деле. Если это так, то вы могли бы назвать runme формы __new__ в myBase:

class myBase(object): 
    def __new__(cls, *args, **kwargs): 
     obj = super(myBase, cls).__new__(cls) 
     obj.__init__(*args, **kwargs) 
     obj.runme() 

    def __init__(self, a): 
     print 'myBase init' 
     self.list = ['myBase', a] 

    def runme(self): 
     print 'myBase:', self.list 

class ChildA(myBase): 
    def __init__(self, a, b): 
     print 'ChildA init' 
     super(ChildA, self).__init__(a) 
     self.list.extend(['ChildA', b]) 

    def runme(self): 
     print 'ChildA:', self.list 

class ChildAA(ChildA): 
    def __init__(self, a, b, c): 
     print 'ChildAA init' 
     super(ChildAA, self).__init__(a, b) 
     self.list.extend(['ChildAA', c]) 

    def runme(self): 
     print 'ChildAA:', self.list 

Вы можете заказать код внутри различных __init__ функций в соответствии с требованиями процесса инициализации, и собственно runme функции всегда будет вызываться после __init__ Завершает :

>>> ChildA(1, 2) 
ChildA init 
myBase init 
ChildA: ['myBase', 1, 'ChildA', 2] 
>>> ChildAA(1, 2, 3) 
ChildAA init 
ChildA init 
myBase init 
ChildAA: ['myBase', 1, 'ChildA', 2, 'ChildAA', 3]