Python | OOP:len

Python Python
スポンサーリンク

概要(lenは「そのオブジェクトの“サイズ”を即座に返す」ための特別メソッド)

lenは、len(obj)が呼ばれたときに使われる“サイズ取得”の入口です。数や件数など「直感的に数えられるもの」を、非負の整数で素早く返します。初心者がまず押さえるべき核心は、返す値の意味と制約(非負のint)、速度(原則O(1))、boolとの関係(空ならFalse)、そして“重い計算を入れない”設計です。


基本の使い方(len()とlenの関係)

最小の実装と動作

class Cart:
    def __init__(self):
        self._items: list[tuple[str, int]] = []
    def add(self, sku: str, qty: int):
        self._items.append((sku, qty))
    def __len__(self) -> int:
        return len(self._items)  # 件数(品目数)を返す

c = Cart()
print(len(c))  # 0
c.add("A001", 2)
print(len(c))  # 1
Python
  • ポイント: len(obj)は内部でobj.len()を呼びます。返す値は「サイズ」を表す非負の整数にします。

非負のintであること(制約)

class Bad:
    def __len__(self) -> int:
        return -1  # 悪い実装:len()は非負を要求
Python
  • 必須条件: lenは非負のintを返すべきです。負を返すと例外になり、float等の型も不可です。

設計の要点(何を“サイズ”とみなすか・速度・意味の一貫性)

“サイズ”の意味を明確に決める

class TextBuffer:
    def __init__(self):
        self._text = ""
    def write(self, s: str):
        self._text += s
    def __len__(self) -> int:
        return len(self._text)  # 文字数(バイト数ではない)
Python
  • ラベル: 文字数なのか、行数なのか、アイテム数なのかを一貫して定義すること。利用者が迷わない指標にします。

原則はO(1)で返す(重い計算をしない)

class TreeBad:
    def __init__(self, root):
        self.root = root
    def __len__(self):
        # NG:毎回ツリー全走査は重い
        return self._count_nodes(self.root)
Python
  • 指針: サイズは保持・キャッシュしておき、len()は即座に返すのが基本。毎回の全走査やI/Oは避けます。

一貫性(メソッド・プロパティと整合)

  • 狙い: list-likeならlenは要素数、文字列なら文字数、集合なら要素数。API全体で直感どおりに揃えると学習コストが下がります。

重要ポイントの深掘り(boolとの関係・コレクション設計・抽象基底)

空かどうかの判定に使われる(boolとの関係)

class Bag:
    def __init__(self):
        self._items = []
    def __len__(self) -> int:
        return len(self._items)

b = Bag()
if b:      # bool(b) は len(b) == 0 かどうかで決まる(0ならFalse、>0ならTrue)
    print("not empty")
Python
  • 重要: boolを実装していなくても、lenが0ならFalse、0より大ならTrueと評価されます。空判定の基礎になります。

コレクションの型としてふるまう(list風・set風)

class Inventory:
    def __init__(self):
        self._set: set[str] = set()
    def add(self, sku: str):
        self._set.add(sku)
    def __len__(self) -> int:
        return len(self._set)
Python
  • 狙い: コレクションを表す型では、lenは「含まれる要素数」にするのが定石です。

Sizedの契約(「長さがある」ことを示す設計)

  • ヒント: collections.abc.Sizedを満たす(lenを持つ)と、「長さを持つもの」として多くの汎用コードから扱いやすくなります。

実務例(Web / APIでのモデル・キュー・キャッシュ)

モデルで件数を返す(コメント・添付など)

class Post:
    def __init__(self, title: str):
        self.title = title
        self._comments: list[str] = []
    def add_comment(self, text: str):
        self._comments.append(text)
    def __len__(self) -> int:
        return len(self._comments)  # コメント数
Python
  • 効果: UIやバッジ表示、条件分岐(空なら表示しない)に自然に使えます。

キューやバッファの残量

class Queue:
    def __init__(self):
        self._q: list[str] = []
    def push(self, x: str): self._q.append(x)
    def pop(self) -> str: return self._q.pop(0)
    def __len__(self) -> int: return len(self._q)  # 残件数
Python
  • 効果: 制御ロジックで「満杯か空か」を即判定できます。

キャッシュの項目数

class Cache:
    def __init__(self):
        self._store: dict[str, str] = {}
    def put(self, k: str, v: str): self._store[k] = v
    def __len__(self) -> int: return len(self._store)  # キャッシュ件数
Python
  • 効果: 監視・ログで「サイズが肥大していないか」を把握できます。

よくある落とし穴と回避(意味のブレ・遅さ・型違反)

“何を数えるか”が曖昧

  • 問題: 文字数なのか行数なのか、品目数なのか数量合計なのかが曖昧だと誤用が起きます。
  • 対策: 名称とドキュメントで明示し、必要なら別プロパティ(lines、total_qty)を分けます。

毎回の重い計算で遅いlen

  • 問題: 全走査・外部I/Oをlenに入れると、printやログだけで重くなります。
  • 対策: サイズは内部構造に合わせて更新・キャッシュ。lenはO(1)で返す。

非intや負の値を返す

  • 問題: float・負数は不正。エラーになります。
  • 対策: 常にintで非負。設計段階で不変条件として固定する。

例題(行指向テキストと商品合計を分けて正しく数える)

“行数”と“数量合計”を別々に設計

class TextLines:
    def __init__(self):
        self._lines: list[str] = []
    def add(self, line: str):
        self._lines.append(line)
    def __len__(self) -> int:
        return len(self._lines)  # 行数
    @property
    def chars(self) -> int:
        return sum(len(s) for s in self._lines)  # 文字数(重いならキャッシュ)

class Cart:
    def __init__(self):
        self._items: list[tuple[str, int]] = []  # (SKU, qty)
        self._count: int = 0                     # 件数キャッシュ
        self._total_qty: int = 0                 # 数量合計キャッシュ
    def add(self, sku: str, qty: int):
        if qty <= 0: raise ValueError("数量は正の数")
        self._items.append((sku, qty))
        self._count += 1
        self._total_qty += qty
    def __len__(self) -> int:
        return self._count            # 品目数(O(1))
    @property
    def total_qty(self) -> int:
        return self._total_qty        # 数量合計(別指標)
Python

このように、「何を数えるか」を分けて定義し、lenは即返せる指標に固定すると、使いやすくて速いクラスになります。


まとめ(lenは“即わかるサイズ”を非負intで返す。重さと曖昧さを排除する)

lenはlen(obj)のための特別メソッドで、「そのオブジェクトのサイズ」を非負のintで、原則O(1)で返します。何を数えるかは一貫して明示し、空判定やUI/ログで活きる設計にする。重い計算やI/Oを入れず、必要な別指標はプロパティ等で分ける。これを徹底すれば、初心者でも「直感的で速く、壊れにくい」サイズ設計が自然に書けます。

タイトルとURLをコピーしました