【问题标题】:Why is __init__ not called after __new__ SOMETIMES为什么在 __new__ 有时不调用 __init__
【发布时间】:2012-02-20 19:22:27
【问题描述】:

先说这不是重复 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?
  • 我知道这也是“一种” AbstractBaseMehtod 机会,但这并没有帮助

无论如何,__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(抽象工厂排序)类作为默认值。事情进展顺利,但这种无法解释的行为是以后几乎可以肯定的错误。

更新: Ignacio,解决了最重要的问题,如果返回的对象不是 cls 的“实例”,则不会调用 __init__。我确实发现调用“构造函数”(例如AA(args..) 是错误的,因为它会再次调用__new__ 而你又回到了你开始的地方。你可以修改一个arg 以采用不同的路径。这只是意味着你调用@ 987654342@ 两次而不是无限次。一个可行的解决方案是将上面的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__ 的类的子类时。我不喜欢上面的编辑,但至少我现在对规则有所了解。

【问题讨论】:

标签: python genshi


【解决方案1】:

来自the documentation

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

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

【讨论】:

  • 好的,我有点得到超级文字“不返回 cls 的实例”。但是,我正在返回一个不同类的新实例,它有自己的__init__,但它不会被调用,即使它是一个公共基础(在旧的 mro 中)。 应该用 new 调用它吗??
  • 你应该在__new__()实例化它,通过调用它的构造函数。
  • 哎哟。这里是个坏主意。我认为这可能会导致我上面引用的“调用两次”问题,但由于 ABC 是一个公共基础,因此在代码中调用构造函数会导致对 __new__ 的递归调用并挂起会话:-(
  • 那你在clsname 之间的检查不正确。
  • cls 和 name 之间的检查不是问题,构造函数似乎仍然错误......请参阅上面的编辑。话虽如此,你确实准确地回答了我的首要问题,这让我回到了正轨。谢谢
【解决方案2】:

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

我快速浏览了 Genshi source code,我在 TemplateLoader 的“类”参数上看到的唯一要求是它可以使用给定的参数调用。

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

【讨论】:

  • 是的,在源代码的深处,它最终会调用 default_class 上的构造函数,该构造函数可以是任何可调用的。我认为这将是一个可行的选择。未来可能会有所不同,并且“似乎”传递 MarkupTemplate 的子类是正确的 OO 事情。但是在我看来,__new__ 的行为应该有所不同,所以我该说谁 ;-)
  • 这似乎没有回答所提出的问题。
猜你喜欢
  • 2010-10-15
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-28
  • 2016-11-06
相关资源
最近更新 更多