关于python:为什么在__new__之后没有调用__init__

Why is __init__ not called after __new__ SOMETIMES

让我从这开始,这不是重复的如果在没有参数的情况下调用了new,为什么不调用init?我试着为__new____init__仔细构造一些示例代码,但我找不到任何解释。

基本参数:

  • 有一个名为notmine的基类,因为它来自另一个库(我将在最后披露,这里不重要)
  • 该类有一个__init__方法,该方法反过来调用_parse方法
  • 我需要在子类中重写_parse方法
  • 在调用之前,我要创建的子类是未知的
  • 我知道有工厂设计方法,但我不能在这里使用(更多在最后)
  • 我试着谨慎使用super以避免python logging:为什么调用了两次?
  • 我知道这也是"某种"抽象的机会,但这没有帮助

不管怎样,应该在__new__之后调用__init__,对于下面的一些样本为什么不起作用的每一个解释,我似乎能够指出其他确实起作用的案例,并排除这种解释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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__,而不是在另一种情况下调用__init__。我看过"呼叫"类的MRO,但它没有提供任何见解,因为我可以调用该类(或CCC中的子类)并调用__init__

结束注释:我使用的NotMine库是genshi markuptemplate,不使用工厂设计方法的原因是他们的templateLoader需要一个defaultclass来构造。直到我开始解析,我才知道,这是我在__new__中做的。有很多很酷的巫术,根希装载机和模板做的,使这值得努力。

我可以运行他们的加载器的一个未修改的实例,并且只要我将abc(抽象类工厂)类作为默认类传递,当前一切都可以工作。事情进展顺利,但这种无法解释的行为后来几乎成了某种缺陷。

更新:Ignacio确定了顶行问题,如果返回的对象不是"cls的实例",则不会调用__init__。我确实发现调用"构造函数"(例如AA(args..)是错误的,因为它将再次调用__new__,而您正好回到您开始的地方。您可以修改参数以采用不同的路径。这意味着你给ABC.__new__打两次电话,而不是无限打。一个有效的解决方案是将上述class ABC编辑为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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__的类的子类时。我不喜欢上面的编辑,但至少我现在的规则好了一点。


从文档:

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

这是一个让__new__()返回a的一个不同的类的新实例,它有其自己的__init__()来代替。你会发现如果你需要创建一个新的CLS和适当的构造函数调用,而不是如果需要。


这只是我的两美分,但是你为什么不genshi鸭打字使用Python提供一类behaves样东西?

我把genshi快速看源代码和唯一的要求,我看到在"阶级"的templateloader参数是它是在给定的参数调用。

我认为它会更容易在一个模拟的类工厂函数返回的是实际创建的实例。