Kodrill

Python のクラスと型ヒント

Python 3 エンジニア認定実践試験

class 構文・属性とメソッド・継承・dataclass・オブジェクト関連関数・型ヒント

問題数
20 問
合格ライン
70%
実行して確認済み
17 問
この分野の要点を読む8 章・約 16 分。実行して確かめたコード例と、試験でよくある引っかけをまとめています

ログインなしで試す

無料の 5 問を、1問ごとに正誤と解説を見ながら解けます。解答は保存されません。

5 問を試す

無料登録して学習を記録する

学習履歴・ダッシュボード・ブックマーク・模試モードが使えます。

★ 残り 15 問はプレミアム会員限定です

無料プランでは先頭 5 問まで解けます。プレミアムなら全問題・苦手問題の復習・広告非表示が使えます。
料金プランを見る

問題と解説を見る (無料)

登録しなくても、次の5問は問題文と解説をそのまま読めます。

Python のクラスと型ヒントの要点

内容の確認日: 2026-10-04

この分野は『Python実践レシピ』の第 4 章 (Python のクラス・3 問) と第 5 章 (型ヒント・2 問) に対応します。基礎試験のクラスの知識に加えて、property などのメソッドの種類、多重継承での super() の動き、dataclass のオプションが問われます。

型ヒントは「実行時には検査されない」という前提を押さえたうえで、list[str]・int | None などの書き方と、typing モジュールの主な型を覚えましょう。

クラス変数とインスタンス変数

クラスの直下で定義した変数はクラス変数で、すべてのインスタンスで共有されます。self.名前 = 値 で作るのはインスタンス変数で、インスタンスごとに別です。

インスタンスから属性を読むときは、インスタンス変数 → クラス変数の順に探します。ただし インスタンス.名前 = 値 と代入すると、クラス変数を書き換えるのではなく、同じ名前のインスタンス変数が新しく作られます。

Python
class Player:    count = 0​    def __init__(self, name):        self.name = name        Player.count += 1​a = Player("ann")b = Player("bob")a.count = 99print(a.count, b.count, Player.count)print(vars(a))
ターミナル
python3 main.py
99 2 2
{'name': 'ann', 'count': 99}

名前の先頭のアンダースコアは慣習で、_name は「内部用」の目印です (アクセスは制限されません)。__name のように 2 つ付けると名前修飾 (ネームマングリング) され、_クラス名__name という名前で保存されるため、サブクラスとの名前の衝突を避けられます。

property・classmethod・staticmethod

@property を付けたメソッドは、obj.area のように属性として読み出せます。@名前.setter で代入時の処理を定義でき、setter がないプロパティに代入すると AttributeError になります。値の検証や、ほかの属性から計算する値に使います。

@classmethod は第 1 引数にクラス (cls) を受け取るメソッドで、cls(...) でインスタンスを作る「代替コンストラクター」によく使います。@staticmethod は self も cls も受け取らない関数を、クラスの中にまとめるときに使います。

Python
class Money:    def __init__(self, yen):        self.yen = yen​    @property    def yen(self):        return self._yen​    @yen.setter    def yen(self, value):        if value < 0:            raise ValueError("負の金額")        self._yen = value​    @classmethod    def from_text(cls, text):        return cls(int(text.removesuffix("円")))​    @staticmethod    def tax(yen):        return yen // 10​m = Money.from_text("1200円")print(m.yen, Money.tax(m.yen))try:    m.yen = -1except ValueError as e:    print(e)
ターミナル
python3 main.py
1200 120
負の金額

__init__ の中の self.yen = yen もプロパティの setter を通るので、生成時の値も検証されます。

特殊メソッド

__ で囲まれた名前のメソッドを定義すると、組み込みの関数や演算子の動きを決められます。

・__repr__・__str__: 表示用の文字列。print() や f-string は __str__ を、リストの中の要素や !r は __repr__ を使う (__str__ がなければ __repr__ が使われる) ・__eq__・__lt__ など: ==・< などの比較 ・__add__ など: + などの演算子 ・__len__・__getitem__・__iter__・__contains__: len()・obj[i]・for・in ・__call__: obj() のように呼び出せるようにする

__eq__ だけを定義すると __hash__ が None になり、そのクラスのインスタンスは辞書のキーや集合の要素に使えなくなります。等しいもの同士は同じハッシュ値を持つ、という規則を守るためです。

Python
class Deck:    def __init__(self, cards):        self.cards = list(cards)​    def __len__(self):        return len(self.cards)​    def __getitem__(self, index):        return self.cards[index]​    def __repr__(self):        return f"Deck({self.cards})"​deck = Deck("ABC")print(len(deck), deck[0], deck[-1], "B" in deck, list(deck))print([deck])
ターミナル
python3 main.py
3 A C True ['A', 'B', 'C']
[Deck(['A', 'B', 'C'])]

__getitem__ を定義すると、__iter__ や __contains__ がなくても、for や in が先頭から順に取り出して動きます。

継承と MRO・抽象基底クラス

super() は「親クラス」ではなく、メソッド解決順序 (MRO) で次のクラスを指します。多重継承では、MRO は クラス.__mro__ で確認でき、各クラスの super() をたどると、すべてのクラスのメソッドが 1 回ずつ呼ばれます。

Python
class Base:    def setup(self):        print("Base")​class Logging(Base):    def setup(self):        print("Logging")        super().setup()​class Cache(Base):    def setup(self):        print("Cache")        super().setup()​class App(Logging, Cache):    pass​App().setup()print([c.__name__ for c in App.__mro__])
ターミナル
python3 main.py
Logging
Cache
Base
['App', 'Logging', 'Cache', 'Base', 'object']

Logging.setup の super() は Base ではなく、MRO で次の Cache を指します。

abc.ABC を継承し、@abstractmethod を付けたメソッドを持つクラスは抽象基底クラスになり、インスタンスを作ろうとすると TypeError になります。抽象メソッドをすべて実装したサブクラスだけがインスタンス化できるので、「このメソッドを必ず実装すること」を強制できます。

dataclass

@dataclass を付けたクラスでは、型ヒント付きのクラス変数がフィールドになり、__init__・__repr__・__eq__ が自動で作られます。主なオプションは次のとおりです。

・frozen=True: フィールドへの代入を禁止する (代入すると FrozenInstanceError)。__hash__ も作られ、辞書のキーに使える ・order=True: < などの比較メソッドを作る。フィールドを定義順に並べたタプルとして比べる ・field(default_factory=list): リストなど変更可能な既定値は、インスタンスごとに作る (= [] と書くとエラー) ・field(init=False)・field(repr=False)・field(compare=False): __init__・__repr__・比較の対象から外す ・__post_init__: 自動生成の __init__ の最後に呼ばれる。ほかのフィールドから値を計算するのに使う

Python
from dataclasses import dataclass, field, asdict​@dataclass(order=True)class Task:    priority: int    title: str = field(compare=False)    tags: list[str] = field(default_factory=list, repr=False)​tasks = [Task(2, "書く"), Task(1, "調べる"), Task(3, "見直す")]print(min(tasks))print([t.title for t in sorted(tasks)])print(asdict(Task(1, "x", ["a"])))
ターミナル
python3 main.py
Task(priority=1, title='調べる')
['調べる', '書く', '見直す']
{'priority': 1, 'title': 'x', 'tags': ['a']}

既定値のないフィールドを、既定値のあるフィールドより後に書くと TypeError になります (__init__ の引数の並びが作れないため)。

オブジェクト関連関数

属性の名前を文字列で扱う関数として、getattr(obj, 名前, 既定値)・setattr(obj, 名前, 値)・hasattr(obj, 名前)・delattr(obj, 名前) があります。getattr は既定値を渡すと、属性がないときに AttributeError ではなく既定値を返します。

型を調べるには、サブクラスも含めて判定する isinstance(obj, 型) を使います。第 2 引数はタプルで複数の型を指定できます。type(obj) is 型 は型が完全に一致するときだけ真になるので、bool の値に対して type(True) is int は False です (bool は int のサブクラス)。

Python
class Settings:    debug = False​s = Settings()for key, value in {"debug": True, "level": "info"}.items():    setattr(s, key, value)print(getattr(s, "debug"), getattr(s, "port", 8000), hasattr(s, "level"))print(vars(s))print(isinstance(True, int), type(True) is int, isinstance(1.5, (int, float)))
ターミナル
python3 main.py
True 8000 True
{'debug': True, 'level': 'info'}
True False True

vars(obj) はインスタンスの属性の辞書 (__dict__) を返し、クラス変数は含みません。ほかに、属性の一覧を返す dir()、呼び出せるかを調べる callable()、オブジェクトの ID を返す id() などがあります。

型ヒント

型ヒントは、引数・戻り値・変数の型を def f(name: str, n: int = 1) -> str: のように書く注釈です。実行時には検査されず、違う型を渡してもエラーにはなりません。誤りは mypy などの静的型チェッカーや、エディターの補完・警告で見つけます。注釈は 関数.__annotations__ から参照できます。

Python 3.9 以降は list[str]・dict[str, int]・tuple[int, ...] のように組み込み型に直接型引数を書け、3.10 以降は int | None のように | で「いずれかの型」を書けます。Optional[int] は int | None と同じ意味です。

typing モジュールの主な型は次のとおりです。

・Any: どんな型とも互換性がある ・Callable[[int, str], bool]: 引数と戻り値の型を指定した呼び出し可能オブジェクト ・Literal["r", "w"]: 決まった値だけを取る ・Final: 再代入しない定数 (実行時には検査されない) ・TypedDict・NamedTuple: キーやフィールドに型を付けた辞書・タプル ・Protocol: 「このメソッドを持っていればよい」という構造的な型 (継承していなくても、同じメソッドを持てば互換とみなされる)

Python 3.12 からは、def first[T](items: list[T]) -> T: のように関数名やクラス名の後ろで型変数を宣言でき、type 名前 = 型 で型エイリアスを定義できます。

Python
from typing import Protocol​class Closable(Protocol):    def close(self) -> None: ...​class File:    def close(self) -> None:        print("ファイルを閉じた")​def close_all[T: Closable](items: list[T]) -> int:    for item in items:        item.close()    return len(items)​def double(n: int) -> int:    return n * 2​print(close_all([File(), File()]))print(double("ab"), double.__annotations__)
ターミナル
python3 main.py
ファイルを閉じた
ファイルを閉じた
2
abab {'n': <class 'int'>, 'return': <class 'int'>}

double("ab") は型ヒントに反しますが、実行時には検査されないので "ab" * 2 が計算されます。[T: Closable] は「T は Closable を満たす型」という制約付きの型変数です。

試験でよくある引っかけ

  • インスタンスからクラス変数に代入する

    a.count = 99 はクラス変数を変えず、a だけのインスタンス変数を作ります。

  • setter のないプロパティ

    @property だけのプロパティに代入すると AttributeError です。

  • super() が指すクラス

    多重継承では、super() は MRO で次のクラスを指します。親クラスとは限りません。

  • 抽象クラスのインスタンス化

    抽象メソッドが残っているクラスをインスタンス化すると TypeError です。

  • dataclass の変更可能な既定値

    tags: list = [] はエラーです。field(default_factory=list) を使います。

  • __eq__ だけを定義したクラス

    __hash__ が None になり、辞書のキーや集合に使えなくなります。

  • type() と isinstance() の違い

    type(True) is int は False、isinstance(True, int) は True です。

  • 型ヒントは実行時に検査されない

    型ヒントと違う型を渡しても、実行時のエラーにはなりません。

問題で確認する

この分野の問題を、解説つきで読めます (登録不要)。

出典・参考

コード例は、実際に実行して結果を確認しています。