12

これは、 なぜ __new__ が no args で呼び出された場合に __init__ が呼び出されないのかの繰り返しではありません。のサンプルコードを慎重に作成しようとしまし__new____init__が、説明がありません。

基本パラメータ:

  • 別のライブラリからのものとして、NotMine と呼ばれる基本クラスがあります (ここでは重要ではありませんが、最後に開示します)。
  • そのクラスには、__init__メソッドを呼び出す_parseメソッドがあります
  • _parseサブクラスでメソッドをオーバーライドする必要がある
  • どのサブクラスを作成しているかは、呼び出しまでわかりません
  • 工場の設計方法があることは知っていますが、ここでは使用できません (詳細は最後に)
  • superPython ロギングの問題を回避するために、 慎重に使用しようとしました: なぜ __init__ が 2 回呼び出されるのですか?
  • これも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__。げんしローダーとテンプレートが行うクールなブードゥー魔法がたくさんあり、これは努力する価値があります。

ローダーの変更されていないインスタンスを実行できます。現在、デフォルトとして ABC (abstract sort-of-factory) クラスのみを渡す限り、すべてが機能します。物事はうまくいっていますが、この説明のつかない動作は、後でほぼ確実に起こるバグです。

更新:イグナシオは、返されたオブジェクトがcls の「インスタンス」でない場合、呼び出されないという一番上の質問に答えまし__init__た。「コンストラクター」の呼び出しAA(args..)は間違っています(たとえば__new__、もう一度呼び出すと、開始した場所に戻るため、間違っています。別のパスを取るように引数を変更できます。これABC.__new__は、無限ではなく 2 回呼び出すことを意味します。解決策は、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__。私は上記の編集が好きではありませんが、少なくともルールは少し良くなりました。

4

2 に答える 2

17

ドキュメントから:

cls__new__()のインスタンスを返さない場合、新しいインスタンスのメソッドは呼び出されません。__init__()

これは、代わりに呼び出される独自のクラスを持つ__new__()のクラスの新しいインスタンスを返すことができるようにするためです。__init__()新しいclを作成しているかどうかを検出し、作成していない場合は代わりに適切なコンストラクターを呼び出す必要があります。

于 2012-02-20T19:28:16.060 に答える
-1

ここに 2 セントしかありませんが、Python のダック タイピングを使用して Genshi にクラスのように動作するものを提供してみませんか?

Genshiのソース コードをざっと見てみると、TemplateLoader の「class」パラメーターで見た唯一の要件は、指定された引数で呼び出し可能であることです。

実際に作成されたインスタンスを返すファクトリ関数を使用してクラスをモックする方が簡単だと思います。

于 2012-02-20T22:09:40.420 に答える