例えば、以下のような抽象基底クラスがあったときに、それを実装するクラスを、 NamedTuple で実装したくなりました。 (より正確には、もともと NamedTuple だったクラスを抽象基底クラスの実装にしたくなった)

from abc import ABCMeta, abstractmethod


class Fooable(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

これは、どうやったら実現できますでしょうか?

まず、単純に上記 Fooable と NamedTuple を多重継承したクラスは、メタクラスが不一致なので作成できません。

from typing import NamedTuple, NamedTupleMeta


class Person(NamedTuple, Fooable):
    age: int
    name: str

# => TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

メタクラスの不一致が原因なので、両方を継承したメタクラスを作ってみると、今度はひとまずクラスの定義は完了し、実際にオブジェクトを作成することはできるものの、

  1. 実際に抽象基底クラスの抽象メソッドを実装しなくてもインスタンスを作成できてしまい、
  2. isinstance で抽象基底クラスのインスタンスであると判定されない

という性質があるため、これは、抽象クラスを用意することのメリットをほぼほぼ打ち消してしまうな、と思っています。特に、 mypy を用いる場合には、 isinstance による条件分岐による型チェックは、これが正しく効かせられないとなると、わりと型を付与するメリットが、かなり減ってしまうなと思っています。

class NamedTupleABCMeta(ABCMeta, NamedTupleMeta):
    pass


class FooablePerson(Fooable, NamedTuple, metaclass=NamedTupleABCMeta):
    name: str
    age: int


fooable_john = FooablePerson(name='John', age=30)

assert isinstance(fooable_john, Fooable)
# => AssertionError

質問

python3 において、抽象基底クラスを実装する、 NamedTuple を作成することはできますか?

再現可能なコード全体

from typing import NamedTuple, NamedTupleMeta
from abc import ABCMeta, abstractmethod


class Fooable(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass


class MyClass(Fooable):
    pass


# my_obj = MyClass()
# => TypeError: Can't instantiate abstract class MyClass with abstract methods foo


# class Person(NamedTuple, Fooable):
#     age: int
#     name: str
#
# => TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases


class NamedTupleABCMeta(ABCMeta, NamedTupleMeta):
    pass


class FooablePerson(Fooable, NamedTuple, metaclass=NamedTupleABCMeta):
    name: str
    age: int


fooable_john = FooablePerson(name='John', age=30)

assert isinstance(fooable_john, Fooable)
# => AssertionError