设计模式 - Python元类中的__call__和__new__的区别?
大家讲道理
大家讲道理 2017-04-18 10:04:49
0
2
608

参考文档

creating-a-singleton-in-python和what-is-a-metaclass-in-python

问题描述

下面这两段代码的执行结果反映了一个问题:很明显元类的存在会影响__call__和__new__的优先级,请问大神能否分析一下两者执行结果不同的原因?

实例代码

1.不含元类的单例模式

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        print('__new__')
        return cls._instance

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
        print('__call__')
        return cls._instance

class Foo(Singleton):
    pass

print('1')
foo1 = Foo()
print('2')
foo2 = Foo()

print(foo1 is foo2)  # True

上面这段代码的执行结果

$ python non_metaclass.py
1
__new__
2
__new__
True

2.含有元类的单例模式

class Singleton(type):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        print('__new__')
        return cls._instance

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
        print('__call__')
        return cls._instance


class Foo(metaclass=Singleton):
    # 不兼容Python2
    pass

print('1')
foo1 = Foo()
print('2')
foo2 = Foo()

print (foo1 is foo2)  # True

上面这段代码的执行结果

$ python metaclass.py
__new__
1
__call__
2
__call__
True

如果描述不够详细,请在评论区留一下言,我再改进。

大家讲道理
大家讲道理

光阴似箭催人老,日月如移越少年。

全員に返信(2)
黄舟

ここで修正して丁寧に説明します

メタクラスはクラス構造の属性を定義し、クラス内の "__new__" メソッドと "__init__" メソッドはクラス インスタンスの処理に使用されます

私たちが定義するすべてのクラスは、

型のインスタンスとして理解できます。 リーリー

それでは、「__new__」と「__call__」に戻りましょう

メタクラスでは、クラスを定義するときに「__new__」が実行されます。2 つのクラスがこのメタクラスを使用する場合は、2 回実行されます。

リーリー

そして、インスタンス化するたびに __call__ が呼び出されます。実際、これは Foo.__new__ と同じです。つまり、Foo が __new__ を定義している場合、メタクラスの __call__ は実行されません。

リーリー

メタクラスの「__new__」は、定義した型を変更できます。ここでは Foo をリストとして定義しました。

リーリー

メタクラスを正しく理解できる人は非常に少ないので、質問してくださってとても感謝しています。

いいねを押す +0
刘奇

これはメタクラスが new と call の優先順位に影響を与えるという問題ではなく、メタクラスの __new__ はクラス作成時に一度しか呼び出されないということです。そして、あなたの質問のように、この __new__ はクラスを作成するために使用されます。いつ作成されましたか?インタプリタは Foo クラスを解釈するときに、クラス定義で __metaclass__ 属性を探し、見つかった場合はそれを使用してクラスを作成します。見つからない場合は、組み込み型を使用してクラスが作成されます。もう一度呼び出さないのはなぜでしょうか? メタクラスがすでにクラスを作成しているため、Foo() が呼び出されるたびにクラスを再作成することはできません。出力からも、 foo2 = Foo() が __new__ を出力しないことがわかります。この時点ではクラスはメタクラス、つまり Singleton によって作成されているため、そのまま使用を開始できます。私たちが普段使っているクラスと何ら変わらないので、クラスが作成されるたびに新しいオブジェクトが作成されます。なぜ __call__ を使うのでしょうか?
Foo はメタクラス Singleton によって作成されたクラスであるため、Foo は Singleton のインスタンス オブジェクトと考えることができます。したがって、Foo() が呼び出されるたびに __call__ が呼び出されます。 __call__ では、作成済みのインスタンス オブジェクトを取得します。ただのシングルトンじゃないの? 。

コードを通して詳しく説明しましょう

リーリー

実行後、__call__ 関数の cls は、メタクラス (Foo) で __new__ を初めて呼び出して作成されたクラスであり、cls._instance は Foo のインスタンス オブジェクトであることがわかります。 Foo() が呼び出されるたびに、同じインスタンス オブジェクトが取得されます。 結局のところ、メタクラスはクラスを作成するクラスです。作成されたクラスは、通常定義するクラスと何ら変わりません。

いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート