クラスの__new__メソッドをファクトリとして使用する:__init__が2回呼び出される 質問する

クラスの__new__メソッドをファクトリとして使用する:__init__が2回呼び出される 質問する

Python で奇妙なバグに遭遇しました。__new__クラスのメソッドをファクトリとして使用すると、__init__インスタンス化されたクラスのメソッドが 2 回呼び出されることになります。

元々のアイデアは__new__、クラス外でファクトリ関数を宣言することなく、渡されたパラメータに応じて親クラスのメソッドを使用して子クラスの特定のインスタンスを返すというものでした。

ここでファクトリ関数を使用するのが最適な設計パターンであることはわかっていますが、プロジェクトのこの時点で設計パターンを変更するとコストがかかります。そこで質問したいのは、このようなスキーマで の二重呼び出しを回避し__init__、 の単一呼び出しのみを取得する方法はあるかということです。__init__

class Shape(object):
    def __new__(cls, desc):
        if cls is Shape:
            if desc == 'big':   return Rectangle(desc)
            if desc == 'small': return Triangle(desc)
        else:
            return super(Shape, cls).__new__(cls, desc)

    def __init__(self, desc):
        print "init called"
        self.desc = desc

class Triangle(Shape):
    @property
    def number_of_edges(self): return 3

class Rectangle(Shape):
    @property
    def number_of_edges(self): return 4

instance = Shape('small')
print instance.number_of_edges

>>> init called
>>> init called
>>> 3

どのような助けでも大歓迎です。

ベストアンサー1

オブジェクトを構築すると、Python は__new__オブジェクトを作成するためにそのメソッドを呼び出して、__init__返されたオブジェクトに対して を呼び出します。 を__new__呼び出して内部からオブジェクトを作成すると、Triangle()さらに と が呼び出され__new__ます__init__

あなたがすべきことは次のとおりです:

class Shape(object):
    def __new__(cls, desc):
        if cls is Shape:
            if desc == 'big':   return super(Shape, cls).__new__(Rectangle)
            if desc == 'small': return super(Shape, cls).__new__(Triangle)
        else:
            return super(Shape, cls).__new__(cls, desc)

これにより、の呼び出しをトリガーせずにRectangleまたはが作成され、 は1 回だけ呼び出されます。Triangle__init____init__

スーパーがどのように機能するかについての@Adrianの質問に答えるために編集します:

super(Shape,cls)検索cls.__mro__して見つけShape、次にシーケンスの残りの部分を検索して属性を見つけます。

Triangle.__mro__は でありですが(Triangle, Shape, object)、はだけです。いずれの場合も、を呼び出すと、mro シーケンス内の までのすべてが無視されるため、残るのは単一の要素タプルのみであり、これを使用して目的の属性が検索されます。Rectangle.__mro__(Rectangle, Shape, object)Shape.__mro__(Shape, object)super(Shape, cls)Shape(object,)

ダイヤモンド継承がある場合、これはさらに複雑になります。

class A(object): pass
class B(A): pass
class C(A): pass
class D(B,C): pass

ここで、B のメソッドは を使用する可能性がありsuper(B, cls)、それが B インスタンスであれば検索します(A, object)が、インスタンスがある場合は のD同じ呼び出しでBが検索されます。(C, A, object)これD.__mro__は であるためです。(B, C, A, object)

したがって、この特定のケースでは、図形の構築動作を変更する新しいミックスイン クラスを定義し、既存のものから継承しながらも異なる方法で構築された特殊な三角形と四角形を作成できます。

おすすめ記事