Hãy để tôi bắt đầu với điều này không phải là sự lặp lại tại sao __init__ không được gọi nếu __new__ được gọi không có args. Tôi đã cố gắng xây dựng cẩn thận một số mã mẫu cho __new__ và __init__ không có lời giải thích nào tôi có thể tìm thấy.
Các tham số cơ bản:
- Có một lớp cơ sở gọi là Notmine vì nó đến từ một thư viện khác (tôi sẽ tiết lộ ở cuối, không quan trọng ở đây)
- Lớp đó có một phương thức __init__ lần lượt gọi phương thức _parse
- Tôi cần ghi đè phương thức _parse trong các lớp con
- lớp con nào tôi tạo ra không được biết đến cho đến khi gọi
- Tôi biết có các phương pháp thiết kế nhà máy nhưng tôi không thể sử dụng chúng ở đây (nhiều hơn ở cuối)
- Tôi đã cố gắng sử dụng cẩn thận super để tránh các vấn đề trong ghi nhật ký Python: Tại sao __init__ được gọi là hai lần?
- Tôi biết đây cũng là "một cơ hội Tóm tắtBaseMhtod nhưng điều đó không giúp được gì
Dù sao, __init__ nên được gọi sau __new__ và đối với mọi giải thích về lý do tại sao một số mẫu dưới đây không hoạt động, tôi dường như có thể chỉ ra các trường hợp khác hoạt động và loại trừ lời giải thích.
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 11Nếu bạn thực thi mã, bạn có thể thấy rằng mỗi cuộc gọi đến __new__, nó sẽ thông báo "cửa nào" nó đang xuất hiện và với loại nào. Tôi có thể thoát khỏi cùng một "cửa" với cùng một đối tượng "loại" và có __init__ được gọi trong một trường hợp chứ không phải cái kia. Tôi đã xem xét MRO của lớp "gọi" và không cung cấp cái nhìn sâu sắc vì tôi có thể gọi lớp đó (hoặc một subcass như trong CCC) và đã gọi __init__.
Ghi chú kết thúc: Thư viện 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 3 tôi đang sử dụng là Genshi Markuptemplate và lý do không sử dụng phương pháp thiết kế nhà máy là Templateloader của họ cần một lớp mặc định để xây dựng. Tôi không biết cho đến khi tôi bắt đầu phân tích cú pháp, điều mà tôi làm trong __new__. Có rất nhiều phép thuật Voodoo tuyệt vời mà Genshi Loaders và Mẫu làm điều đó làm cho điều này xứng đáng với nỗ lực. The 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 3 library I'm using is the Genshi MarkupTemplate and the reason for not using a Factory design method is that their TemplateLoader needs a defaultClass to construct. I don't know until I start parsing, which I do in __new__. There is a lot of cool voodoo magic that genshi loaders and templates do that make this worth the effort.
Tôi có thể chạy một thể hiện không sửa đổi của trình tải của họ và hiện tại mọi thứ đều hoạt động miễn là tôi chỉ vượt qua lớp ABC (Tóm tắt Sắp xếp-Vactory) làm mặc định. Mọi thứ đang hoạt động tốt nhưng hành vi không giải thích được này là một lỗi gần như chắc chắn sau đó.
Cập nhật: Ignacio, đóng đinh câu hỏi dòng trên cùng, nếu đối tượng được trả về không phải là "thể hiện" CLS thì __init__ không được gọi. Tôi thấy rằng gọi "hàm tạo" (ví dụ: 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 6 là sai vì nó sẽ gọi lại __new__ và bạn đã quay lại ngay khi bạn bắt đầu. Bạn có thể sửa đổi một con đường khác. Điều đó chỉ có nghĩa là bạn gọi 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 8 hai lần thay vì vô hạn . Một giải pháp làm việc là chỉnh sửa 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 9 ở trên như: Ignacio, nailed the top line question, if the returned object is not an "instance of" cls then __init__ is not called. I do find that calling the "constructor" (e.g. 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 6 is wrong as it will call __new__ again and you are right back where you started. You could modify an arg to take a different path. That just means you call 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 8 twice rather than infinitely. A working solution is to edit 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 9 above as:
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 11Lưu ý một vài dòng cuối cùng. Không gọi __init__ nếu đó là một lớp "khác" không có ý nghĩa với tôi, đặc biệt là khi lớp "khác nhau" vẫn là một lớp con của lớp gọi __init__. Tôi không thích chỉnh sửa ở trên nhưng ít nhất tôi có được các quy tắc tốt hơn một chút bây giờ.