LoginSignup
6
8

More than 5 years have passed since last update.

super(A, self) で消耗した話

Posted at

言いたいこと

super(A, self)ではなくsuper() と書いた方が良い。

経緯

諸事情により、Aという名前のクラスを、同一モジュール内で2回以上宣言しなければならないとします。
大まかにはこういうコードが書かれます。

fool1.py
class A:
    pass

a11 = A()
a12 = A()


class A:
    pass

a21 = A()
a22 = A()

クラス定義というのは、言い換えれば(typeによる)クラスオブジェクトの生成ですから、2つのAクラスは別オブジェクトです。
当然、こうなります。

>>> a11.__class__ is a12.__class__
True
>>> a21.__class__ is a22.__class__
True
>>> a11.__class__ is a21.__class__
False
>>> a12.__class__ is a22.__class__
False

このことを意識せずにsuper(A, self) を使って予想外の動きをするのに悩まされました。
例えば、次のような定義を考えます。

class Asuper:
    pass


class A(Asuper):
    def get_super1(self):
        return super(A, self)

    def get_super2(self):
        return super()

    def get_super3(self):
        return super(self.__class__, self)

Aをインスタンス化してget_super[123] を呼び出すと以下のようになります。

>>> a = A()
>>> a.get_super1()
<super: <class 'A'>, <A object>>
>>> a.get_super2()
<super: <class 'A'>, <A object>>
>>> a.get_super3()
<super: <class 'A'>, <A object>>

次に、Aを1回インスタンス化した後にAを再定義してから同じことをやってみます。

>>> a = A()
>>> class A:
...     pass
... 
>>> a.get_super1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in get_super1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> a.get_super2()
<super: <class 'A'>, <A object>>
>>> a.get_super3()
<super: <class 'A'>, <A object>>

super(A, self)を使っているget_super1だけTypeErrorが発生しました。おそらく、実行中のモジュールにおけるAという名前が参照するオブジェクトが切り替わったことにより制約違反が発生しました。
これを回避するには、get_super3のように、オブジェクトが参照するクラスオブジェクトを指定すると良いと思います。未確認ですがsuperの引数を省略するとこのような挙動になってるのではないかと思います。

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8