2012-02-20 7 views
8

Позвольте мне начать с этого не повторение Why does __init__ not get called if __new__ called with no args. Я попытался тщательно построить образец кода для __new__ и __init__, который не имеет объяснений, которые я могу найти.Почему __init__ не вызывается после __new__ ИНОГДА

Основные параметры:

  • Существует базовый класс называется NotMine, как это происходит из другой библиотеки (я буду раскрывать в конце концов, не важно)
  • Этот класс имеет __init__ метод, в свою очередь, называет _parse метод
  • мне нужно переопределить метод _parse в подклассах
  • который подкласс Я создаю не известно до вызова
  • Я знаю, что есть методы проектирования завода, но я не могу использовать их здесь (Подробнее в конце)
  • Я попытался сделать бережное использование super, чтобы избежать проблем в Python logging: Why is __init__ called twice?
  • Я знаю, что это также «вид 'возможность AbstractBaseMehtod, но это не помогло

Во всяком случае, __init__ должен быть вызван после __new__ и для каждого объяснения того, почему некоторые образцы ниже не работают, я, кажется, быть в состоянии указать на другие случаи, которые делают работу и исключить объяснение.

class NotMine(object): 

    def __init__(self, *args, **kwargs): 
     print "NotMine __init__" 
     self._parse() 

    def _parse(self): 
     print "NotMine _parse" 

class ABC(NotMine): 
    def __new__(cls,name,*args, **kwargs): 
     print "-"*80 
     print "Entered through the front door ABC.__new__(%s,%s,*%s,**%s)"%(cls,name,args,kwargs) 
     if name == 'AA': 
      obj = super(NotMine,ABC).__new__(AA,*args,**kwargs) 
      print "Exiting door number 1 with an instance of: %s"%type(obj) 
      return obj 
     elif name == 'BB': 
      obj = super(NotMine,ABC).__new__(BB,*args,**kwargs) 
      print "Exiting door number 2 with an instance of: %s"%type(obj) 
      return obj 
     else: 
      obj = super(NotMine,ABC).__new__(cls,*args,**kwargs) 
      print "Exiting door number 3 with an instance of: %s"%type(obj) 
      return obj 

class AA(ABC): 

    def _parse(self): 
     print "AA _parse" 

class BB(ABC): 

    def __init__(self, *args, **kw): 
     print "BB_init:*%s, **%s"%(args,kw)   
     super(BB,self).__init__(self,*args,**kw) 

    def _parse(self): 
     print "BB _parse" 

class CCC(AA): 

    def _parse(self): 
     print "CCCC _parse" 


print("########### Starting with ABC always calls __init__ ############") 
ABC("AA")   # case 1 
ABC("BB")   # case 2 
ABC("NOT_AA_OR_BB") # case 3 

print("########### These also all call __init__ ############") 
AA("AA")   # case 4 
BB("BB")   # case 5 
AA("NOT_AA_OR_BB") # case 6 
BB("NOT_AA_OR_BB") # case 7 
CCC("ANYTHING") # case 8 

print("########### WHY DO THESE NOT CALL __init__ ############") 
AA("BB") # case 9 
BB("AA") # case 10 
CCC("BB") # case 11 

Если выполнить код, вы можете увидеть, что для каждого вызова __new__ он объявляет «какой дверь» она выходит через и с каким типом. Я могу выйти из той же «двери» с тем же «типом» объекта и вызвать __init__ в одном случае, а не в другом. Я посмотрел на mro класса «call», и это не дает понимания, поскольку я могу вызвать этот класс (или подкасс как в CCC) и вызвал __init__.

Конец Примечания: NotMine библиотека Я использую является Genshi MarkupTemplate и причина не используя метод проектирования завода является то, что их TemplateLoader нуждается в defaultClass построить. Я не знаю, пока не начну синтаксический анализ, что я делаю в __new__. Существует много классной волшебной магии вуду, что genshi загрузчики и шаблоны делают это стоит усилий.

Я могу запустить немодифицированный экземпляр своего загрузчика, и в настоящее время все работает до тех пор, пока я ТОЛЬКО передаю класс ABC (абстрактный сортировочный завод) по умолчанию. Все работает хорошо, но это необъяснимое поведение является почти определенной ошибкой позже.

UPDATE: Игнасио, прибил вопрос верхней строки, если возвращаемый объект не является «экземпляр» ЦБСА тогда __init__ не называется. Я нахожу, что вызов «конструктора» (например, AA(args..) ошибочен, так как он снова вызовет __new__, и вы вернетесь туда, где вы начали. Вы можете изменить аргумент, чтобы выбрать другой путь. Это просто означает, что вы вызываете ABC.__new__ дважды, а не бесконечно ,Рабочий раствор для редактирования class ABC выше, как:

class ABC(NotMine): 
    def __new__(cls,name,*args, **kwargs): 
    print "-"*80 
    print "Entered through the front door ABC.__new__(%s,%s,*%s,**%s)"%(cls,name,args,kwargs) 
    if name == 'AA': 
     obj = super(NotMine,ABC).__new__(AA,*args,**kwargs) 
     print "Exiting door number 1 with an instance of: %s"%type(obj) 
    elif name == 'BB': 
     obj = super(NotMine,ABC).__new__(BB,*args,**kwargs) 
     print "Exiting door number 2 with an instance of: %s"%type(obj) 
    elif name == 'CCC': 
     obj = super(NotMine,ABC).__new__(CCC,*args,**kwargs) 
     print "Exiting door number 3 with an instance of: %s"%type(obj) 
    else: 
     obj = super(NotMine,ABC).__new__(cls,*args,**kwargs) 
     print "Exiting door number 4 with an instance of: %s"%type(obj) 
    ## Addition to decide who calls __init__ ## 
    if isinstance(obj,cls): 
     print "this IS an instance of %s So call your own dam __init__"%cls 
     return obj 
    print "this is NOT an instance of %s So __new__ will call __init__ for you"%cls 
    obj.__init__(name,*args, **kwargs) 
    return obj 

print("########### now, these DO CALL __init__ ############") 
AA("BB") # case 9 
BB("AA") # case 10 
CCC("BB") # case 11 

Обратите внимание на последние несколько строк. Не вызывать __init__, если это «другой» класс, для меня не имеет смысла, ОСОБЕННО, когда «другой» класс по-прежнему является подклассом класса, вызывающего __init__. Мне не нравятся приведенные выше изменения, но, по крайней мере, я немного поправляю правила.

+0

Является ли Генши использованием метакласса? См. Http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – Borealid

+0

Нет, мой пример кода не использует genshi в качестве базы. –

ответ

7

От the documentation:

__new__() Если не возвращает экземпляр ЦБС, затем __init__() метод нового экземпляра не будет вызван.

Это позволит __new__() к return a new instance of a different class, который имеет свой собственный __init__() называться вместо этого. Вам нужно будет определить, создаете ли вы новые cls и вместо этого вызываете соответствующий конструктор, если нет.

+0

ОК, я как бы получаю супер литерал «не возвращает экземпляр cls». Но я возвращаю новый экземпляр другого класса, у которого есть его '__init__', но он не вызван, даже если он является общей базой (в старом mro). Am ** Я ** должен был назвать это новым? –

+0

Вы должны * инстанцировать * его в '__new __()', вызывая его конструктор. –

+0

ouch. Плохая идея здесь. Я думал, что это может привести к проблеме «вызываемого дважды», которую я цитировал выше, но поскольку ABC является общей базой, вызов конструктора в коде вызывает рекурсивный вызов '__new__' и зависает сеанс :-( –

0

Только мои два цента здесь, но почему вы не используете печатание утиных Python, чтобы обеспечить Genshi тем, что ведет себя как класс?

Я быстро взглянул на Genshi source code, и единственными требованиями, которые я видел в параметре «class» к TemplateLoader, является то, что он может быть вызван с заданными аргументами.

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

+0

да, в глубине источника он в конечном итоге вызывает конструктор на default_class, который может быть любым вызываемым. Я думаю, что это будет жизнеспособной альтернативой. Будущее может быть другим, и «кажется», что передача подкласса MarkupTemplate - это правильная вещь OO. Но снова мне показалось, что '__new__' должно было вести себя по-другому, чтобы я мог говорить ;-) –