2012-02-20 15 views
8

Bunun ile başlayalım, Why does __init__ not get called if __new__ called with no args tekrarı değil. __new__ ve __init__ için bazı örnek kodları dikkatlice inceleyip, bulabildiğim bir açıklama yapmadım.Neden __init__ __new__ sonra çağrılmıyor __new__ SOMETIMES

temel parametreleri:

başka kütüphaneden geldiği gibi NotMine adında bir temel sınıf vardır
  • (Burada önemli değil, sonunda ifşa edeceğiz)
  • O sınıf bir __init__ yöntemi vardır sırayla
  • Ben alt sınıflarda _parse yöntemini geçersiz gereken bir _parse yöntemini çağırır
  • hangi ı çağırma kadar bilinmemektedir yaratıyorum alt sınıf
  • Ben fabrika tasarımı yöntemleri vardır biliyorum ama bu 'da türüdür biliyoruz Python logging: Why is __init__ called twice?
  • I sorunlardan kaçınmak için super dikkatli faydalanmak çalıştık
  • (sonunda fazla) onları burada kullanamazsınız 'bir AbstractBaseMehtod fırsat ama bu __new__ sonra BAZI numuneler aşağıdaki I işi diğer durumlarda işaret etmek mümkün gibi görünüyor çalışmıyor neden her açıklama için

Neyse, __init__ çağrılmalıdır yardım etmedi ve açıklamayı çıkar.

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 

Eğer kodu çalıştırmak, bunu "hangi kapıyı" içinden ve ne tür ile çıkılıyor açıkladı __new__ yapılan her çağrı için olduğunu görebilirsiniz. Aynı "tip" nesneyle aynı "kapı" dan çıkabilir ve diğer bir durumda değil, bir örnekte __init__ olmalıdır. Ben "çağrı" sınıfının mro baktım ve bu sınıf (veya CCC olarak bir altkümenin) çağırmak ve denilen __init__ var beri hiçbir anlayış sunuyor.

sonu Notlar: Ben kullanıyorum NotMine kütüphane Genshi MarkupTemplate ve Fabrika tasarım yöntemi kullanmama sebebi onların TemplateLoader inşa etme defaultClass gerekiyor. Ayrışmaya başladığıma kadar bilmiyorum, __new__'da. Genshi yükleyicileri ve şablonlar bu çabaya değer kılan bir sürü voodoo sihir var.

Yükleyicilerinin değiştirilmemiş bir örneğini çalıştırabilirim ve şu anda her şey YALNIZCA öntanımlı olarak ABC (soyut fabrika sınıfı) sınıfını geçtiği sürece çalışır. İşler iyi çalışıyor ama bu açıklanamayan davranış daha sonra neredeyse kesin bir hatadır.

GÜNCELLEME: döndürülen nesne sonra __init__ çağrılmaz cls'ye bir "örneği" değilse Ignacio, üst çizgi soruyu çivilenmiş. Ben "constructor" çağıran (yeniden __new__ yeniden çağırır gibi yanlış ve başlattığınız yerde geri bulursunuz. Farklı bir yol almak için bir arg değiştirebilir. Bu sadece sonsuz yerine iki kez ABC.__new__ diyorsunuz demektir .Bir çalışma çözüm olarak yukarıdaki class ABC düzenlemektir:

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 

Bildirimi Son birkaç satır. __init__, "farklı" bir sınıfsa, "farklı" sınıf hala __init__ çağrı sınıfının bir alt sınıfı olduğunda, ESPECIALLY benim için anlam ifade etmiyor. Yukarıdaki düzenlemeyi sevmiyorum ama en azından kuralları biraz daha iyi anlıyorum. the documentation itibaren

+0

Genshi bir metaclass kullanıyor mu? Bkz. Http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – Borealid

+0

Hayır, Örnek kodum genshi'yi temel olarak kullanmıyor. –

cevap

7

:

__new__() Eğer cls'ye bir örneğini dönmez, ardından yeni örneğinin __init__() yöntem çağrıldığında edilmeyecektir.

Bu

yerine çağrılacak __init__() kendi sahip olduğu, return a new instance of a different class için __new__() sağlamaktır. Yeni bir cl oluşturup oluşturmadığınızı ve uygun yapıcıyı çağırıp çağırmayacağınızı bulmanız gerekecektir.

+0

Tamam, süper edebi türden bir "cls örneğini döndürmez" olsun. Ama, ben '__init__' kendi kendine sahip olan farklı bir sınıfın yeni bir örneğini döndürüyorum, ama ortak bir üs olsa bile (eski mroda) bile çağrılmadı. Am ** Ben ** yeni olarak adlandırmak gerekiyordu? –

+0

Yapmanız gereken yapıcıyı çağırmak için * __new __() 'de * yazmanız gerekir. –

+0

ouch. Kötü fikir burada. Bunun, yukarıda alıntıladığım "iki kez çağrılan" soruna yol açabileceğini düşündüm, ancak ABC ortak bir temel olduğundan, koddaki kurucuyu çağırmak '__new__' için bir çare çağrısına neden oluyor ve oturumu kapatıyor :-( –

0

Sadece iki sentim burada, fakat neden Genshi'yi bir sınıf gibi davranan bir şeyle sağlamak için yazarak Python ördekini kullanmıyorsun?

Genshi source code'a hızlı bir bakış attı ve TemplateLoader'a 'class' parametresinde gördüğüm tek gereksinim, belirtilen argümanlar ile çağrılabilir olmasıdır.

Sanırım bir asıl oluşturulmuş örneği döndüren bir fabrika işlevi ile bir sınıfla uğraşmak daha kolay olurdu.

+0

evet, kaynakta derinden sonunda yapıcıyı çağırıyor herhangi bir çağrılabilir olabilecek default_class üzerinde. Bunun uygun bir alternatif olacağını düşünüyorum. Gelecek farklı olabilir ve MarkupTemplate'in bir alt sınıfını geçirmenin doğru bir OO olayı olduğunu görüyoruz. Ama yine de bana “görünüyordum”, “__new__” farklı davranmalıydı, ben kiminle konuşacağım ;-) –

İlgili konular