首先声明,这不是为什么没有参数调用__new__时__init__不会被调用的重复问题。我已经努力构建了一些样例代码来使用__new__
和__init__
,但无法找到任何解释。
基本要求:
- 有一个名为NotMine的基类,因为它来自另一个库(我会在最后透露,这里不重要)
- 该类具有一个
__init__
方法,该方法又调用_parse
方法 - 我需要在子类中覆盖
_parse
方法 - 创建哪个子类在调用时是未知的
- 我知道有工厂设计模式,但我不能在这里使用它们(更多内容在结尾处)
- 我尝试了谨慎使用
super
以避免Python日志记录:为什么__init__会被调用两次?中出现的问题 - 我知道这也是“抽象基础方法”的机会,但那并没有帮助
总之,__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,但没有发现任何有见地的地方,因为我可以调用该类(或子类,例如CCC)并调用__init__
。
注:
我使用的NotMine
库是Genshi MarkupTemplate,不使用工厂设计方法的原因是他们的TemplateLoader需要一个defaultClass来构造。而我要在解析之前才能知道,而这正是我在__new__
中做的。genshi加载器和模板的许多酷炫的巫术使得这个值得努力去做。
我可以运行一个未修改的实例,并且只要将ABC(抽象的、类似于工厂的)类作为默认类传递,一切都正常工作。但这个无法解释的行为几乎肯定会成为以后的bug。
更新:
Ignacio已经理解了最初的问题,如果返回的对象不是cls的“实例”,那么将不会调用__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__
的类的子类时。我不喜欢上面的修改,但至少现在我更好地理解了规则。