为什么在__new__之后有时不调用__init__ [英] Why is __init__ not called after __new__ SOMETIMES

查看:117
本文介绍了为什么在__new__之后有时不调用__init__的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我从这里开始不是重复 如果__new__为什么不调用__init__没有参数调用.我试图为__new____init__精心构造一些示例代码,但找不到我能找到的解释.

Let me start with this is not a repeat of Why does __init__ not get called if __new__ called with no args. I have tried to carefully construct some sample code for __new__ and __init__ that has no explanation I can find.

基本参数:

  • 有一个名为NotMine的基类,因为它来自另一个库(我将在最后公开,在这里不重要)
  • 该类具有__init__方法,该方法又调用_parse方法
  • 我需要在子类中覆盖_parse方法
  • 要创建的子类在调用之前是未知的
  • 我知道有一些工厂设计方法,但是我不能在这里使用(最后更多)
  • 我尝试仔细使用super以避免出现以下问题: Python日志记录:为什么__init__被调用两次?
  • 我知道这也是'AbstractBaseMehtod'的机会,但这没有帮助
  • There is a base class called NotMine as it comes from another library (I'll disclose at the end, not important here)
  • That class has an __init__ method that in turn calls a _parse method
  • I need to override the _parse method in subclasses
  • which subclass I'm creating is not known until invocation
  • I know there are factory design methods but I cannot use them here (More at the end)
  • I have tried to make careful use of super to avoid the problems in Python logging: Why is __init__ called twice?
  • I know this is also 'kind of' an AbstractBaseMehtod opportunity but that did not help

无论如何,应该在__new__之后调用__init__,并且对于为什么以下某些示例不起作用的每个解释,我似乎都可以指出其他确实起作用的情况并排除了解释.

Anyway, __init__ should be called after __new__ and for every explanation of why SOME samples below don't work I seem to be able to point to other cases that do work and rule out the explanation.

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__.

If you execute the code, you can see that for each call to __new__ it announces "which door" it is exiting through and with what type. I can exit the same "door" with the same "type" object and have __init__ called in one case and not the other. I've looked at the mro of the "calling" class and that offers no insight since I can invoke that class ( or a subcass as in CCC ) and have __init__ called.

尾注: 我正在使用的NotMine库是 Genshi MarkupTemplate,以及未使用Factory设计方法的原因是他们的TemplateLoader需要一个defaultClass来构造.我不知道,直到我开始解析,这在__new__中进行. genshi装载程序和模板可以完成许多令人赞叹的伏都教徒魔法,这值得付出努力.

End Notes: The NotMine 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.

我可以运行其加载程序的未修改实例,并且只要我仅将ABC(抽象的工厂分类)类作为默认类传递,那么当前所有内容都可以正常运行.事情运行良好,但是这种无法解释的行为几乎可以肯定是以后的错误.

I can run an unmodified instance of their loader and currently everything works as long as I ONLY pass the ABC (abstract sort-of-factory) class as the default. Things are working well but this unexplained behavior is an almost certain bug later.

更新: Ignacio提出了最重要的问题,如果返回的对象不是 cls 的实例",则不会调用__init__.我确实发现,调用构造函数"(例如AA(args..)是错误的,因为它将再次调用__new__,而您又回到了开始的位置.您可以修改arg以采用其他路径.这只是意味着您调用ABC.__new__两次,而不是无限地.一个可行的解决方案是将上面的class ABC编辑为:

UPDATE: 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. AA(args..) 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 ABC.__new__ twice rather than infinitely. A working solution is to edit class ABC 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 11

注意最后几行.如果它是不同"类,则不调用__init__对我来说没有意义,尤其是当不同"类仍是调用__init__的类的子类时.我不喜欢上面的编辑,但是至少我现在使规则更好了.

Notice the last few lines. Not calling __init__ if it's a "different" class does not make sense to me, ESPECIALLY when the "different" class is still a subclass of the class calling __init__. I don't like the above edit but least I get the rules a little better now.

推荐答案

来自文档:

如果__new__()不返回 cls 的实例,则将不会调用新实例的__init__()方法.

If __new__() does not return an instance of cls, then the new instance’s __init__() method will not be invoked.

这是为了允许__new__() 返回不同类的新实例 ,它具有自己的__init__()来代替.您将需要检测是否要创建新的cls,否则请调用适当的构造函数.

This is to allow __new__() to return a new instance of a different class, which has its own __init__() to be called instead. You will need to detect if you're creating a new cls, and call the appropriate constructor instead if not.

这篇关于为什么在__new__之后有时不调用__init__的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆