どういうこと??
Pythonでのクラスの継承は次のように書きます。
class B:
"""The base class."""
class C(B):
"""A new class that inherits B."""
ここでのB
はクラス、言い換えればtype
型である必要が...ありません。
クラス以外を継承する方法
適当なオブジェクトを用意します。
class _X:
pass
B = _X()
このオブジェクトに__mro_entries__
を追加します。__mro_entries__
は、(クラス以外のオブジェクトを含む)全て継承元の型を引数に、(クラスのみからなる)実際の継承元の型を返すようにします。この特殊メソッドが継承時に呼び出されます。
B.__mro_entries__ = lambda bases: (str,)
これでstr
のサブクラスが定義できます。
class C(B):
pass
c = C("abc")
type(c) # Out: __main__.C
__mro_entries__
さえあれば何でもよいので、__mro_entries__
を無理やりセットできるものであればなんでも行けます。たとえば、こういうやばい使い方もできます。
import math
math.__mro_entries__ = lambda bases: (str,)
class C(math):
pass
c = C("abc")
type(c) # Out: __main__.C
実際にどう使われるの?
継承の時点で、たとえば多重継承を検出してエラーにすることとかができます。
def _mro_entries(bases: tuple):
if len(bases) > 1:
raise TypeError("Multiple inheritance is forbidden")
return (str,)
B.__mro_entries__ = _mro_entries
通常はメタクラスを定義することで実現されますが、継承元を確認するだけならこっちの方が楽だと思います。
ちなみに標準実装のNamedTuple
も、常に継承する形で使われますが、その実態は関数です。
from typing import NamedTuple
type(NamedTuple) # Out: function
そのため、怖いことにisinstance
チェックができません。注意しましょう。
class Point(NamedTuple):
x: int
y: int
p = Point(x=0, y=1)
isinstance(p, NamedTuple) # TypeError: isinstance() arg 2 must be a type or tuple of types