web-dev-qa-db-ja.com

複数の__init__引数を持つタプルのサブクラス化

次のコードは機能します。

class Foo(Tuple):

    def __init__(self, b):
        super(Foo, self).__init__(Tuple(b))

if __== '__main__':
    print Foo([3, 4])

$ python play.py 

結果:

play.py:4: DeprecationWarning: object.__init__() takes no parameters
  super(Foo, self).__init__(Tuple(b))
(3, 4)

しかし、次のことではありません

class Foo(Tuple):

    def __init__(self, a, b):
        super(Foo, self).__init__(Tuple(b))

if __== '__main__':
    print Foo(None, [3, 4])

$ python play.py 

結果:

Traceback (most recent call last):
  File "play.py", line 7, in <module>
    print Foo(None, [3, 4])
TypeError: Tuple() takes at most 1 argument (2 given)

どうして?

49

タプルは不変なので、代わりに___new___をオーバーライドする必要があります。

python docs

object.__new__(cls[, ...])

クラスclsの新しいインスタンスを作成するために呼び出されます。 __new__()は、インスタンスが要求されたクラスを最初の引数として受け取る静的メソッド(特別なケースであるため、宣言する必要はありません)です。残りの引数は、オブジェクトコンストラクタ式に渡される引数(クラスの呼び出し)です。 __new__()の戻り値は、新しいオブジェクトインスタンス(通常はclsのインスタンス)である必要があります。

通常の実装では、適切な引数を指定して__new__()を使用し、スーパークラスのsuper(currentclass, cls).__new__(cls[, ...])メソッドを呼び出し、必要に応じて新しく作成されたインスタンスを変更してから返すことにより、クラスの新しいインスタンスを作成します。

__new__()clsのインスタンスを返す場合、新しいインスタンスの__init__()メソッドは__init__(self[, ...])のように呼び出されます。ここで、selfは新しいインスタンスであり、残りの引数は、__new__()に渡されたものと同じです。

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

__new__()は、主に不変タイプのサブクラス(intstrTupleなど)がインスタンスの作成をカスタマイズできるようにすることを目的としています。また、クラスの作成をカスタマイズするために、カスタムメタクラスで一般的にオーバーライドされます。

66
John La Rooy

タプル値を割り当てるには、__new__メソッドをオーバーライドする必要があります。

class Foo(Tuple):

    def __new__ (cls, a, b):
        return super(Foo, cls).__new__(cls, Tuple(b))

引数は、Tupleクラスの__init__実装では無視されているようですが、初期化を行う必要がある場合は、次のように実行できます。

class Foo(Tuple):

    def __new__ (cls, a, b):
        return super(Foo, cls).__new__(cls, Tuple(b))

    def __init__(self, a, b):
        self.a=a
        self.b=b

if __== '__main__':
    foo = Foo(None, [3, 4])
    print foo
    print foo.a
    print foo.b
49
Jonatan Anauati