为什么在“new”之后不调用“init”

2024-06-12 17:00:24 发布

您现在位置:Python中文网/ 问答频道 /正文

让我从这开始不是重复 Why does __init__ not get called if __new__ called with no args。我试着为__new____init__构造一些示例代码,但找不到任何解释。

基本参数:

  • 有一个基类叫做NotMine,因为它来自另一个库(我将在最后透露,这里不重要)
  • 该类有一个__init__方法,该方法反过来调用_parse方法
  • 我需要重写子类中的_parse方法
  • 我要创建的子类在调用之前是未知的
  • 我知道有一些工厂设计方法,但我不能在这里使用它们(最后说的更多)
  • 我试着小心使用super,以避免 Python logging: Why is __init__ called twice?
  • 我知道这也是一个基于抽象的机会,但那没有帮助

无论如何,__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__时,它都会宣布“哪个门”正在退出,以及退出的类型是什么。我可以用同一个“type”对象退出同一个“door”,并在一种情况下调用__init__,而不是在另一种情况下调用。我已经看过了“calling”类的mro,它没有提供任何细节,因为我可以调用该类(或CCC中的子类)并调用__init__

尾注: 我使用的NotMine库是GenshiMarkupTemplate,而不使用工厂设计方法的原因是它们的TemplateLoader需要一个defaultClass来构造。我不知道,直到我开始解析,我在__new__中做的。有很多很酷的巫毒魔法,根希加载程序和模板做的,使这值得努力。

我可以运行他们的加载程序的一个未修改的实例,目前只要我只将ABC(抽象工厂排序)类作为默认类,一切都可以正常工作。事情进展顺利,但这种无法解释的行为几乎是一个确定的错误。

更新: 如果返回的对象不是“cls的“实例”,则不调用__init__。我确实发现调用“构造函数”(例如AA(args..))是错误的,因为它将再次调用__new__,而您又回到了开始的位置。您可以修改arg以采用不同的路径。这只意味着你调用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__如果它是“different”类对我来说没有意义,特别是当“different”类仍然是调用__init__的类的子类时。我不喜欢上面的编辑,但至少我现在对规则有点了解了。


Tags: nameobjnewparseinitargskwargsaa
2条回答

来自the documentation

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

这是允许__new__()return a new instance of a different class,它有自己的__init__()被调用。您需要检测是否正在创建新的cls,如果没有,则调用相应的构造函数。

这里只有我的两分钱,但是为什么不使用Python duck类型来为Genshi提供行为类似于类的东西呢?

我快速地看了一下Genshisource code,我在TemplateLoader的'class'参数上看到的唯一要求是它可以用给定的参数调用。

我认为用工厂函数返回实际创建的实例来模拟类会更容易。

相关问题 更多 >