2012-02-20 13 views
8

Zacznę od tego, że nie jest to powtórka z Why does __init__ not get called if __new__ called with no args. Próbowałem starannie zbudować przykładowy kod dla __new__ i __init__, który nie ma wyjaśnienia, które mogę znaleźć.Dlaczego __init__ nie jest wywoływane po __new__ SOMETIMES

Podstawowe parametry:

  • Istnieje klasa bazowa nazywana NotMine gdyż pochodzi z innej biblioteki (będę ujawniać na końcu, nie ważne tutaj)
  • To klasa ma metodę __init__ że z kolei nazywa _parse metoda
  • muszę zastąpić metodę w podklasach _parse
  • których podklasa tworzę nie jest znany aż do wywołania
  • wiem, że istnieją metody projektowania fabryki, ale nie mogę ich używać tutaj (Więcej na końcu)
  • Próbowałem dokonać starannego wykorzystania super aby uniknąć problemów w Python logging: Why is __init__ called twice?
  • wiem, że to też „niby "okazją AbstractBaseMehtod ale to nie pomogło

Zresztą __init__ powinny być nazywane po __new__ i za każdym wyjaśnieniem, dlaczego niektóre próbki poniżej nie działają wydaje mi się, aby móc zwrócić się do innych przypadków, które wykonują pracę i wykluczyć wyjaśnienie.

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 

Jeśli wykonanie kodu, można zobaczyć, że dla każdego wywołania __new__ zapowiada „który door” jest wyjściem poprzez iz jakiego typu. Mogę wyjść z tych samych "drzwi" z tego samego obiektu "typu" i mieć __init__ w jednym przypadku, a nie w drugim. Spojrzałem na mro z klasy "calling" i nie oferuje mi wglądu, ponieważ mogę wywołać tę klasę (lub podklas jak w CCC) i wywołać __init__.

End Uwagi: NotMine biblioteki używam jest Genshi MarkupTemplate i powód nie stosując metodę projektowania Factory to, że ich TemplateLoader potrzebuje defaultClass skonstruować. Nie wiem, dopóki nie rozpocznie się parsowanie, co robię w __new__. Istnieje wiele fajnych magii voodoo, które obsługują ładowarki i szablony genshi, co czyni ten wysiłek wartym wysiłku.

Mogę uruchomić niezmodyfikowaną instancję swojego programu ładującego, a obecnie wszystko działa tak długo, jak TYLKO przekazuję klasę ABC (abstrakcyjne sortowanie fabryczne) jako domyślną. Wszystko działa dobrze, ale to niewyjaśnione zachowanie jest prawie pewnym błędem później.

UPDATE: Ignacio, przybity górnym wierszu pytanie, czy zwrócony obiekt nie jest „przypadkiem” CLS następnie __init__ nie jest tzw. Uważam, że wywołanie "konstruktora" (np. AA(args..) jest nieprawidłowe, ponieważ będzie ono wywoływać ponownie __new__ i jesteś z powrotem tam, gdzie zacząłeś.Możesz zmodyfikować argument, aby wybrać inną ścieżkę.To oznacza, że ​​dwa razy zamiast wywołać ABC.__new__, a nie bez końca .Roztwór roboczy jest edycja class ABC powyżej jako:

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 

Wskazówka ostatnie kilka linii. Nie wywoływanie __init__, jeśli jest to "inna" klasa, nie ma dla mnie sensu, SZCZEGÓLNIE, gdy "inna" klasa nadal jest podklasą klasy wywołującej __init__. Nie podoba mi się powyższa edycja, ale przynajmniej teraz mam nieco lepsze zasady.

+0

Czy Genshi używa metaclass? Zobacz http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – Borealid

+0

Nie, Mój przykładowy kod nie używa genshi jako bazy. –

Odpowiedz

7

Od the documentation:

Jeśli __new__() nie zwraca instancję CLS, następnie __init__() metoda nowej instancji nie będzie się powoływać.

Pozwala to __new__() do return a new instance of a different class, który ma swój własny __init__() nazywać zamiast. Będziesz musiał wykryć, czy tworzysz nowe cls i zamiast tego wywołaj odpowiedni konstruktor.

+0

OK, otrzymuję bardzo dosłowne "nie zwraca instancji cls". Ale zwracam nową instancję innej klasy, która ma swój własny "__init__", ale nie jest wywoływana, nawet jeśli jest to wspólna baza (w starym mro). Mam ** ja ** powinienem nazwać to nowym? –

+0

Powinieneś * utworzyć instancję * w '__new __()', wywołując jego konstruktor. –

+0

ouch. Zły pomysł tutaj. Pomyślałem, że może to prowadzić do problemu "wywołanego dwa razy", o którym wspomniałem powyżej, ale ponieważ ABC jest wspólną podstawą, wywołanie konstruktora w kodzie powoduje wywołanie rekursywne do '__new__' i zawiesza sesję :-( –

0

Tylko moje dwa centy tutaj, ale dlaczego nie używasz pisania literami Python, aby zapewnić Genshi coś, co zachowuje się jak klasa?

Szybko rzuciłem okiem na Genshi source code, a jedynymi wymaganiami, które widziałem w parametrze "klasa" dla TemplateLoader, jest to, że można je wywoływać z podanymi argumentami.

Myślę, że łatwiej byłoby kpić z klasy z funkcją fabryczną zwracającą rzeczywistą utworzoną instancję.

+0

tak, głęboko w źródle, które ostatecznie wywołuje konstruktor na default_class, który może być dowolnym wywołanym. Myślę, że byłaby to realna alternatywa. Przyszłość może być inna i "wydaje się", że przejście podklasy MarkupTemplate jest właściwą rzeczą do zrobienia. Ale znowu "wydawało się" mi, że "__new__" powinien zachowywać się inaczej, więc kim jestem, aby mówić ;-) –

Powiązane problemy