Pythonの抽象クラスとは、abc.ABC を継承し @abstractmethod を付けたメソッドを持つクラスで、「子クラスで必ず実装すべきメソッド」を決めるための仕組みです。一方 typing.Protocol は、継承しなくても「同じメソッドを持っていれば同じ型とみなす」ための型ヒントの仕組みです。

ABCは 実行時に(インスタンスを作る瞬間に)実装漏れを TypeError で知らせ、Protocolは mypy や pyright などの型チェッカーで実行前に 知らせます。この記事では、通常の継承・ABC・Protocol・@runtime_checkable・ダックタイピングの5つを 1つの比較表 にまとめ、Python 3.11.15 と 3.14.7 の実行結果、mypy 1.20.2 と pyright 1.1.414 の実際のエラー出力 で違いを確認します。目的から選べる判断表、多重継承と super() の注意点、collections.abc の抽象基底クラス一覧(必要なメソッドを実行して取得)までまとめました。

型ヒント全般や dataclass の書き方は別記事に譲り、ここでは 「インターフェース(約束ごと)をどう表すか」 に絞って解説します。

ABCとProtocolの違い早見表

5つの方法の比較表

観点 通常の継承 ABC(抽象クラス) Protocol @runtime_checkable Protocol ダックタイピング(型なし)
書き方 class B(A) class A(ABC) + @abstractmethod class P(Protocol) @runtime_checkable class P(Protocol) 何も書かない
実装側の継承 必要 必要(または register) 不要 不要 不要
実装漏れに気づくタイミング 呼んだとき インスタンス生成時に TypeError 型チェック時(mypy/pyright) 型チェック時 呼んだときに AttributeError
isinstance で判定 できる できる TypeError できる(メソッドの有無だけ) ―
引数・戻り値の型の照合 しない しない する(型チェッカー) 実行時はしない しない
共通処理(既定実装)を持てる はい はい 明示的に継承した場合のみ 同左 ―
既存クラス・外部ライブラリに後から当てる 難しい register で可(検査なし) そのまま当てられる そのまま当てられる ―
主な用途 実装の再利用 継承前提の枠組み・プラグイン基底 引数の型・依存の差し替え 実行時の分岐も必要なとき 小さなスクリプト

目的別の選び方

やりたいこと 選ぶもの 理由
子クラスに必ずメソッドを実装させたい ABC 実装漏れはインスタンス生成時に TypeError
共通の処理を親に書き、一部だけ子に任せたい ABC(テンプレートメソッド) 抽象メソッドと通常メソッドを混在できる
既存クラスや外部ライブラリのクラスに、後から型を当てたい Protocol 相手のコードを変えずに済む
関数の引数に「このメソッドがあればよい」と書きたい Protocol 構造で判定(構造的部分型)
プラグインを基底クラスで登録・列挙したい ABC __subclasses__() や isinstance で扱える
テスト用の偽物(フェイク)を差し込みたい Protocol + フェイククラス/ABC なら Mock(spec=...) 継承なしで型チェックが通る
実行時に「〜を持っているか」で分岐したい @runtime_checkable Protocol ただし名前の有無しか見ない

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Python 抽象クラスとProtocol|ABCとの違い・使い分け早見表」

ABC(抽象クラス)の書き方と実行結果

抽象メソッドと抽象プロパティを定義する

ABC を継承し、実装を子クラスに任せたいメソッドに @abstractmethod を付けます。 プロパティを抽象にするときは @property の内側(下)に @abstractmethod を書きます。

import sys
from abc import ABC, abstractmethod

print(sys.version.split()[0])

class Exporter(ABC):
    @abstractmethod
    def export(self, rows: list[dict]) -> str: ...

    @property
    @abstractmethod
    def extension(self) -> str: ...

    def filename(self, base: str) -> str:
        return f"{base}.{self.extension}"

try:
    Exporter()
except TypeError as e:
    print("TypeError:", e)

class CsvExporter(Exporter):
    def export(self, rows):
        return "\n".join(",".join(map(str, r.values())) for r in rows)

try:
    CsvExporter()
except TypeError as e:
    print("TypeError:", e)

class CsvExporter2(CsvExporter):
    extension = "csv"

e = CsvExporter2()
print(e.filename("orders"), repr(e.export([{"id": 1, "qty": 3}, {"id": 2, "qty": 5}])))
print(sorted(Exporter.__abstractmethods__), sorted(CsvExporter.__abstractmethods__), sorted(CsvExporter2.__abstractmethods__))
print(isinstance(e, Exporter))

実行結果(Python 3.11.15):

3.11.15
TypeError: Can't instantiate abstract class Exporter with abstract methods export, extension
TypeError: Can't instantiate abstract class CsvExporter with abstract method extension
orders.csv '1,3\n2,5'
['export', 'extension'] ['extension'] []
True

実行結果(Python 3.14.7):

3.14.7
TypeError: Can't instantiate abstract class Exporter without an implementation for abstract methods 'export', 'extension'
TypeError: Can't instantiate abstract class CsvExporter without an implementation for abstract method 'extension'
orders.csv '1,3\n2,5'
['export', 'extension'] ['extension'] []
True
  • 抽象メソッドが1つでも残っていると、そのクラスはインスタンスを作れません。 エラーには残っているメソッド名が出ます(3.12以降は「without an implementation for ...」という文面です)。
  • 抽象プロパティは、子クラスでクラス属性 extension = "csv" を置くだけでも満たせます。 名前が定義されれば抽象ではなくなるためです。
  • __abstractmethods__ で、まだ実装されていないメソッド名を確認できます。 デバッグに便利です。
  • filename() のような通常メソッドは親に書けます。 共通処理は親、差分は子という「テンプレートメソッド」の形です。

abstractproperty の代わりに @property + @abstractmethod

abc.abstractproperty は Python 3.3 から非推奨で、代わりに @property と @abstractmethod を重ねます。 クラスメソッドや静的メソッドも同じように重ねられ、@abstractmethod は一番内側(関数のすぐ上)に書きます。

抽象にしたいもの 書き方(上から順に) 非推奨の書き方
プロパティ @property → @abstractmethod @abstractproperty
クラスメソッド @classmethod → @abstractmethod @abstractclassmethod
静的メソッド @staticmethod → @abstractmethod @abstractstaticmethod
from abc import ABC, abstractmethod

class Base(ABC):
    @classmethod
    @abstractmethod
    def create(cls): ...

    @staticmethod
    @abstractmethod
    def version() -> str: ...

    @abstractmethod
    def hello(self):
        return "Base.hello"

class Impl(Base):
    @classmethod
    def create(cls):
        return cls()

    @staticmethod
    def version():
        return "1.0"

    def hello(self):
        return "Impl + " + super().hello()

print(Impl.create().hello(), Impl.version())
Impl + Base.hello 1.0

抽象メソッドにも中身を書けて、子クラスから super().hello() で呼べます。 「共通の前処理は親に書いておき、必ず子でも上書きさせる」ときに使えます。

ABCを継承しないと @abstractmethod は効かない

from abc import ABC, abstractmethod, update_abstractmethods

class NoABC:
    @abstractmethod
    def run(self): ...

print("NoABC():", type(NoABC()).__name__)

class Late(ABC):
    pass

def m(self): ...
Late.m = abstractmethod(m)
print("Late():", type(Late()).__name__)
update_abstractmethods(Late)
try:
    Late()
except TypeError as e:
    print("TypeError:", e)

実行結果(Python 3.11.15):

NoABC(): NoABC
Late(): Late
TypeError: Can't instantiate abstract class Late with abstract method m

@abstractmethod はメタクラス ABCMeta(ABC を継承すると付く)と組み合わせたときだけ効きます。 また、クラスを作ったあとに抽象メソッドを追加しても反映されず、abc.update_abstractmethods()(3.10+)を呼ぶと反映されます。

register で既存クラスを「仮想サブクラス」にする

from abc import ABC, abstractmethod

class Sized2(ABC):
    @abstractmethod
    def __len__(self): ...

Sized2.register(list)
print(isinstance([1], Sized2), issubclass(list, Sized2), list.__mro__)
True True (<class 'list'>, <class 'object'>)

register すると、継承していないクラスも isinstance で True になります。 ただしメソッドを持っているかの検査はなく、MRO(メソッドの探索順)にも ABC は入らないので、親の通常メソッドは使えません。

Protocolの書き方と実行結果

継承しなくても同じメソッドを持てば OK

Protocol は「send(to, message) -> bool を持つもの」のように、形だけを約束します。 実装側のクラスは Protocol を継承する必要がありません。

import sys
from typing import Protocol

print(sys.version.split()[0])

class Notifier(Protocol):
    def send(self, to: str, message: str) -> bool: ...

class MailNotifier:
    def send(self, to: str, message: str) -> bool:
        print(f"[mail] {to}: {message}")
        return True

class ChatNotifier:
    def send(self, to: str, message: str) -> bool:
        print(f"[chat] @{to} {message}")
        return True

class Printer:
    def print_out(self, text: str) -> None:
        print(text)

def notify_all(n: Notifier, members: list[str]) -> int:
    return sum(n.send(m, "在庫が入荷しました") for m in members)

print(notify_all(MailNotifier(), ["a@example.com"]))
print(notify_all(ChatNotifier(), ["sato", "kato"]))
try:
    notify_all(Printer(), ["x"])
except AttributeError as e:
    print("AttributeError:", e)
try:
    Notifier()
except TypeError as e:
    print("TypeError:", e)
try:
    isinstance(MailNotifier(), Notifier)
except TypeError as e:
    print("TypeError:", e)

実行結果(Python 3.11.15。3.14.7でも1行目以外は同じ):

3.11.15
[mail] a@example.com: 在庫が入荷しました
1
[chat] @sato 在庫が入荷しました
[chat] @kato 在庫が入荷しました
2
AttributeError: 'Printer' object has no attribute 'send'
TypeError: Protocols cannot be instantiated
TypeError: Instance and class checks can only be used with @runtime_checkable protocols

実行時には Protocol は何も検査しないため、Printer を渡しても send を呼ぶ瞬間まで気づけません(AttributeError)。Protocol の効果は、次の型チェッカーで発揮されます。Protocol 自体はインスタンス化できず、@runtime_checkable なしで isinstance に使うと TypeError です。

mypy・pyright で実装漏れを検出する

Protocol の違反は mypy や pyright で実行前に見つけます。 次のファイルを shapes.py として保存し、チェックします。ABC の実装漏れも型チェッカーが指摘してくれます。

from abc import ABC, abstractmethod
from typing import Protocol


class Notifier(Protocol):
    def send(self, to: str, message: str) -> bool: ...


class MailNotifier:
    def send(self, to: str, message: str) -> bool:
        return True


class Printer:
    def print_out(self, text: str) -> None: ...


class LooseNotifier:
    def send(self, to: str) -> None: ...


def notify(n: Notifier) -> bool:
    return n.send("sato", "入荷しました")


notify(MailNotifier())
notify(Printer())
notify(LooseNotifier())


class Exporter(ABC):
    @abstractmethod
    def export(self) -> str: ...


class JsonExporter(Exporter):
    pass


JsonExporter()

このファイルを python3 shapes.py で実行すると、27行目の notify(Printer()) で AttributeError になるまで何も起きません。型チェッカーなら実行せずに3か所すべてを指摘します。

pip install mypy
mypy shapes.py

mypy 1.20.2 の出力:

shapes.py:27: error: Argument 1 to "notify" has incompatible type "Printer"; expected "Notifier"  [arg-type]
shapes.py:28: error: Argument 1 to "notify" has incompatible type "LooseNotifier"; expected "Notifier"  [arg-type]
shapes.py:28: note: Following member(s) of "LooseNotifier" have conflicts:
shapes.py:28: note:     Expected:
shapes.py:28: note:         def send(self, to: str, message: str) -> bool
shapes.py:28: note:     Got:
shapes.py:28: note:         def send(self, to: str) -> None
shapes.py:40: error: Cannot instantiate abstract class "JsonExporter" with abstract attribute "export"  [abstract]
Found 3 errors in 1 file (checked 1 source file)
npx -y pyright shapes.py

pyright 1.1.414 の出力(パスを短く省略):

shapes.py:27:8 - error: Argument of type "Printer" cannot be assigned to parameter "n" of type "Notifier" in function "notify"
    "Printer" is incompatible with protocol "Notifier"
      "send" is not present (reportArgumentType)
shapes.py:28:8 - error: Argument of type "LooseNotifier" cannot be assigned to parameter "n" of type "Notifier" in function "notify"
    "LooseNotifier" is incompatible with protocol "Notifier"
      "send" is an incompatible type
        Type "(to: str) -> None" is not assignable to type "(to: str, message: str) -> bool"
          Function accepts too few positional parameters; expected 2 but received 1
            Function return type "None" is incompatible with type "bool" (reportArgumentType)
shapes.py:40:1 - error: Cannot instantiate abstract class "JsonExporter"
    "Exporter.export" is not implemented (reportAbstractUsage)
3 errors, 0 warnings, 0 informations
違反 実行時(python) mypy pyright
メソッドがない(Printer) 呼んだときに AttributeError arg-type エラー "send" is not present
引数・戻り値が違う(LooseNotifier) 呼び方次第で TypeError 期待と実際のシグネチャを表示 足りない引数・戻り値の型まで表示
ABC の実装漏れ(JsonExporter()) インスタンス生成時に TypeError abstract エラー reportAbstractUsage

Protocol は引数の数や戻り値の型まで照合してくれます。 ここが、次の @runtime_checkable との大きな違いです。

runtime_checkable はメソッドの有無しか見ない

@runtime_checkable を付けた Protocol は isinstance で使えますが、確認するのは「その名前の属性があるか」だけです。 引数や戻り値の型、呼び出せるかどうかまでは見ません。

from typing import Protocol, runtime_checkable

@runtime_checkable
class Closable(Protocol):
    def close(self) -> None: ...

class File:
    def close(self) -> None: ...

class WrongSig:
    def close(self, force: bool, reason: str) -> int:
        return 0

class AttrOnly:
    close = 123

print(isinstance(File(), Closable), isinstance(WrongSig(), Closable), isinstance(AttrOnly(), Closable), isinstance(object(), Closable))
print(issubclass(File, Closable))

@runtime_checkable
class HasName(Protocol):
    name: str

class User:
    def __init__(self):
        self.name = "sato"

print(isinstance(User(), HasName))
try:
    issubclass(User, HasName)
except TypeError as e:
    print("TypeError:", e)

実行結果(Python 3.11.15):

True True True False
True
True
TypeError: Protocols with non-method members don't support issubclass()

Python 3.14.7 では最後の行が次のように、対象の属性名まで表示されます。

TypeError: Protocols with non-method members don't support issubclass(). Non-method members: 'name'.

引数の違う WrongSig も、ただの数値 close = 123 を持つ AttrOnly も True になりました。 実行時の isinstance は「入口の振り分け」程度に使い、正しさは型チェッカーで確かめるのが安心です。データ属性を持つ Protocol は issubclass が使えない点も覚えておきましょう。

Protocol を明示的に継承して既定実装を使う

from typing import Protocol

class Greeter(Protocol):
    def name(self) -> str: ...

    def greet(self) -> str:
        return f"こんにちは、{self.name()}さん"

class Member(Greeter):
    def name(self) -> str:
        return "佐藤"

print(Member().greet())

class Half(Greeter):
    pass

print("Half():", type(Half()).__name__)
こんにちは、佐藤さん
Half(): Half

Protocol を明示的に継承すると、Protocol に書いた通常メソッド(greet)を既定実装として使えます。 ただし実装漏れ(Half)は実行時には止まらないので、実行時にも確実に止めたいなら ABC を使います。mypy 1.20.2 では次のように検出されます(ファイル名 greeter.py)。

greeter.py:18: error: Cannot instantiate abstract class "Half" with abstract attribute "name"  [abstract]

Python 3.13以降の補助関数

import typing
from typing import Protocol, runtime_checkable

@runtime_checkable
class Closable(Protocol):
    def close(self) -> None: ...

class File:
    def close(self) -> None: ...

print(typing.get_protocol_members(Closable), typing.is_protocol(Closable), typing.is_protocol(File))

実行結果(Python 3.14.7):

frozenset({'close'}) True False

typing.get_protocol_members() と typing.is_protocol() は 3.13 で追加された関数で、Protocol のメンバー一覧や、Protocol かどうかを調べられます。

多重継承と super() の注意点

Mixin と ABC を組み合わせるときは super() を協調させる

多重継承では、メソッドは MRO(Method Resolution Order)の順に探されます。 各クラスの __init__ で super().__init__(**kw) を呼ぶと、MRO の全クラスの初期化が1回ずつ実行されます。

from abc import ABC, abstractmethod

class Storage(ABC):
    def __init__(self, name: str, **kw):
        super().__init__(**kw)
        self.name = name
        print("Storage.__init__")

    @abstractmethod
    def save(self, key: str, data: bytes) -> None: ...

class LoggingMixin:
    def __init__(self, **kw):
        super().__init__(**kw)
        print("LoggingMixin.__init__")

    def log(self, msg):
        print(f"[{type(self).__name__}] {msg}")

class MemoryStorage(LoggingMixin, Storage):
    def __init__(self, name: str):
        super().__init__(name=name)
        self.items: dict[str, bytes] = {}

    def save(self, key, data):
        self.items[key] = data
        self.log(f"saved {key}")

s = MemoryStorage("mem")
s.save("a", b"1")
print([c.__name__ for c in MemoryStorage.__mro__])

実行結果(Python 3.11.15。3.14.7でも同じ):

Storage.__init__
LoggingMixin.__init__
[MemoryStorage] saved a
['MemoryStorage', 'LoggingMixin', 'Storage', 'ABC', 'object']
  • MRO は MemoryStorage → LoggingMixin → Storage → ABC → object の順です。 LoggingMixin の super() は、親クラスではない Storage を指します。
  • print が super() の後にあるため、表示は MRO の逆順(奥から)になります。
  • 引数はキーワード(name=name)と **kw で受け渡すと、どの順に並んでも壊れにくくなります。
  • Mixin は ABC より左に書くのが一般的です(Mixin のメソッドを優先させるため)。

別のメタクラスを持つクラスと ABC を混ぜる

from abc import ABC, ABCMeta

class Meta(type):
    pass

class WithMeta(metaclass=Meta):
    pass

try:
    class Both(WithMeta, ABC):
        pass
except TypeError as e:
    print("TypeError:", e)

class MetaABC(Meta, ABCMeta):
    pass

class Both2(WithMeta, ABC, metaclass=MetaABC):
    pass

print(type(Both2).__name__)
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
MetaABC

ABC のメタクラスは ABCMeta なので、独自メタクラスを持つクラスと混ぜると metaclass conflict になります。 両方を継承したメタクラスを作って指定すると解決します。Protocol で済む場面なら、継承自体が不要なのでこの問題は起きません。

テストのモック・フェイクと組み合わせる

ABC には Mock(spec=...) や create_autospec が、Protocol には「継承しないフェイククラス」が相性のよい組み合わせです。

from abc import ABC, abstractmethod
from typing import Protocol
from unittest.mock import Mock, create_autospec

class Storage(ABC):
    @abstractmethod
    def save(self, key: str, data: bytes) -> None: ...

m = Mock(spec=Storage)
print(isinstance(m, Storage))
m.save("k", b"x")
m.save.assert_called_once_with("k", b"x")
try:
    m.load("k")
except AttributeError as e:
    print("AttributeError:", e)

auto = create_autospec(Storage, instance=True)
try:
    auto.save("k")
except TypeError as e:
    print("TypeError:", e)

class Clock(Protocol):
    def now(self) -> str: ...

class FixedClock:
    def now(self) -> str:
        return "2026-10-01T09:00"

def stamp(c: Clock, text: str) -> str:
    return f"{c.now()} {text}"

print(stamp(FixedClock(), "棚卸し開始"))

実行結果(Python 3.11.15。3.14.7でも同じ):

True
AttributeError: Mock object has no attribute 'load'
TypeError: missing a required argument: 'data'
2026-10-01T09:00 棚卸し開始
テストの道具 ABC と Protocol と
Mock(spec=X) isinstance も True、存在しない属性は AttributeError 使えるが、型チェッカー上は Mock 型
create_autospec(X, instance=True) 引数の数まで検査 同様に使える
フェイククラス(自作) ABC の継承が必要 継承なしで mypy が通る

時刻・外部通知・保存先など、テストで差し替えたい依存は Protocol で受け取ると、本物とフェイクを自由に入れ替えられます。

collections.abc の抽象基底クラス一覧

collections.abc には、リストや辞書のような「コンテナの形」を表す抽象基底クラスが用意されています。 次の表の「抽象メソッド」は、各クラスの __abstractmethods__ を実際に出力して作りました(Python 3.11.15。Buffer は 3.12+ のため 3.14.7 で確認)。

クラス 抽象メソッド(自分で書くもの) 継承するともらえる主なメソッド 当てはまる組み込み型の例
Container __contains__ ― list、str、dict
Hashable __hash__ ― tuple、str(list は対象外)
Iterable __iter__ ― list、dict、set、ジェネレータ
Iterator __next__ __iter__ iter([]) の戻り値
Reversible __iter__、__reversed__ ― list、dict
Generator send、throw close、__iter__、__next__ ジェネレータ
Sized __len__ ― list、dict、str
Callable __call__ ― 関数、クラス
Collection __contains__、__iter__、__len__ ― list、set、dict
Sequence __getitem__、__len__ __contains__、__iter__、__reversed__、index、count list、tuple、str、range
MutableSequence __getitem__、__setitem__、__delitem__、__len__、insert append、extend、pop、remove、reverse など list
Set __contains__、__iter__、__len__ <=、&、|、-、isdisjoint など frozenset、set
MutableSet __contains__、__iter__、__len__、add、discard remove、pop、clear、|= など set
Mapping __getitem__、__iter__、__len__ get、keys、items、values、__contains__、== dict
MutableMapping __getitem__、__setitem__、__delitem__、__iter__、__len__ pop、popitem、clear、update、setdefault dict
Awaitable __await__ ― コルーチン
AsyncIterable __aiter__ ― 非同期ジェネレータ
AsyncIterator __anext__ __aiter__ 非同期ジェネレータ
Buffer(3.12+) __buffer__ ― bytes、bytearray

Mapping を継承して辞書のように使えるクラスを作る

from collections.abc import Mapping, Iterable, Sequence, Sized, Callable, Hashable

class Inventory(Mapping):
    def __init__(self, data):
        self._d = dict(data)

    def __getitem__(self, k):
        return self._d[k]

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

inv = Inventory({"pen": 12, "note": 5})
print(inv.get("pen"), inv.get("x", 0), list(inv.keys()), list(inv.items()), "pen" in inv, inv == {"pen": 12, "note": 5})
print(isinstance(inv, Mapping), isinstance(inv, dict))
print(isinstance([1], Iterable), isinstance("ab", Sequence), isinstance({1}, Sequence), isinstance(len, Callable), isinstance([], Hashable), isinstance((1,), Hashable))

class Bag:
    def __len__(self):
        return 3

print(isinstance(Bag(), Sized), isinstance(Bag(), Iterable))

class Half(Mapping):
    def __getitem__(self, k): ...

try:
    Half()
except TypeError as e:
    print("TypeError:", e)

実行結果(Python 3.11.15):

12 0 ['pen', 'note'] [('pen', 12), ('note', 5)] True True
True False
True True False True False True
True False
TypeError: Can't instantiate abstract class Half with abstract methods __iter__, __len__
  • 3つのメソッドを書くだけで、get・keys・items・in・== がそろいました。 読み取り専用の辞書風クラスを作るときに便利です。
  • Bag は継承していないのに isinstance(Bag(), Sized) が True です。 Sized や Iterable などの単純な ABC は __subclasshook__ で「メソッドを持っているか」を見ています。
  • 型ヒントでも collections.abc を使えます。 引数を list[str] ではなく Iterable[str] にすると、リスト・タプル・ジェネレータのどれでも受け付けられます。

よくある質問

Q. 抽象クラスの実装漏れはいつエラーになりますか? A. ABC ではインスタンスを作った瞬間に TypeError になります(Can't instantiate abstract class ...)。メソッドを呼ぶまで待たずに気づけます。Protocol は実行時には検査しないので、mypy や pyright で確認します。

Q. ABC と Protocol はどちらを使えばよいですか? A. 実装クラスに継承させて共通処理を持たせたいなら ABC、既存クラスや外部のクラスにも当てたい・テストで差し替えたいなら Protocol が向いています。両方を組み合わせ、引数の型は Protocol、実装の土台は ABC という使い方もできます。

Q. abstractproperty はまだ使えますか? A. Python 3.14 にも残っていますが、3.3 から非推奨です。 @property の下に @abstractmethod を重ねて書きます。

Q. runtime_checkable を付ければ実行時に型を保証できますか? A. 保証できるのは「その名前の属性があるか」までです。 実行結果のとおり、引数が違うメソッドや数値の属性でも True になります。引数や戻り値の照合は mypy・pyright で行います。

まとめ

  • ABC は「継承させて実装を強制する」仕組み。実装漏れはインスタンス生成時に TypeError
  • 抽象プロパティは @property + @abstractmethod。abstractproperty は非推奨
  • Protocol は「継承なしで形が合えば OK」。違反は mypy・pyright が引数や戻り値まで照合して指摘
  • @runtime_checkable の isinstance は名前の有無だけを見る。正しさは型チェッカーで確認
  • 多重継承は MRO の順に super() がつながる。**kw で受け渡し、Mixin を左に書く
  • collections.abc を継承すると、少ない抽象メソッドで get・items などがそろう

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Python 抽象クラスとProtocol|ABCとの違い・使い分け早見表」

株式会社RJCでは、Pythonの抽象クラスとProtocolのような設計の考え方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?