0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#0232(2025/09/03)getattr()ガイド

Posted at

getattr()ガイド:動的属性アクセスでPythonを“使いこなす”

getattr()とは、オブジェクトから属性を文字列で柔軟かつ安全に取得するための組み込み関数である。

なぜgetattr()を知るべきか

ドット演算子による属性アクセスは読みやすいが、属性名が実行時に決まる場面では力不足になる。たとえば、設定ファイルで「使う戦略名」を受け取り、その名前に一致するメソッドを呼びたい。ここでgetattr()を使うと、getattr(obj, "method_name")()のように動的ディスパッチが書ける。反射(リフレクション)を安全に扱える点も魅力で、デフォルト値を指定すれば存在しない属性にも優雅に対処できる。

基本のシグネチャと挙動

getattr(obj, name[, default]) は、obj.name と同等の取得を行う。default を省略し、かつ属性が見つからない場合は AttributeError を送出する。default を与えると例外を出さずにその値を返す。プロパティ、デスクリプタ、__getattr__/__getattribute__ といったフックの挙動も尊重されるため、通常の属性アクセスと同じ規約で動く。

class Greeter:
    prefix = "Hi"
    def greet(self, name): return f"{self.prefix}, {name}!"
g = Greeter()
print(getattr(g, "prefix"))        # => "Hi"
print(getattr(g, "greet")("Ada"))  # => "Hi, Ada!"
print(getattr(g, "missing", None)) # => None(例外を出さない)

代表的ユースケース

  • 動的ディスパッチ:コマンド名→メソッド呼び分け。プラグインやCLIで便利。
  • 設定とオブジェクトのバインド:文字列キーで属性を切り替え、挙動を差し替える。
  • シンプルなDI(依存性注入):実装名を渡して実体を引く。テスト時はモック名に差し替える。
  • シリアライズ支援:モデルのフィールド名リストを回して値を抽出。
  • Web/MLパイプライン:ステージ名やレイヤ名で操作を選ぶ。学習オプションを文字列から解決。
class Commands:
    def add(self, a, b): return a + b
    def mul(self, a, b): return a * b

def run(cmd_name, *args):
    fn = getattr(Commands(), cmd_name, None)
    if callable(fn): return fn(*args)
    raise ValueError(f"unknown command: {cmd_name}")

類似手段との違い(同階層の比較)

手段 主目的 エラー時 複数属性 コール可能取得 遅延解決 型安全性
obj.attr 静的アクセス 例外 不可 通常 高(静的解析が効きやすい)
getattr(obj, "name", d) 動的アクセス 既定値/例外 不可 通常 低〜中(文字列依存)
hasattr(obj, "name") 存在確認 例外なし 不可 非該当 通常
operator.attrgetter("a.b") 取得関数生成 例外 可能(カンマ区切り) 通常
vars(obj) __dict__取得 KeyError(辞書操作次第) 可能 非該当 通常
__getattr__ 最終手段の解決 実装次第 不可 高(動的合成) 実装依存
__getattribute__ 全アクセスのフック 実装次第 不可 最高(すべて介入) 実装難度高

備考

  • operator.attrgetter("a.b") はドットでネストに対応。attrgetter("x", "y") で複数同時取得が可能。
  • vars() はクラスが __slots__ を使っていると欠けることがある。プロパティ等は見えない。

ベストプラクティス

  1. 外部入力はホワイトリストで:ユーザー入力の属性名をそのまま解決しない。許可名だけを列挙し、if name in allowed で判定してから getattr
  2. デフォルト値を積極活用:存在しない可能性があるなら、例外より既定値で処理を続行するほうが堅牢。
  3. callable で実行可否を確認:得た属性が関数とは限らない。呼び出すなら必ず callable(attr) を確認。
  4. attrgetter でパイプラインを簡潔にmap(attrgetter("name"), objects) のように関数として渡せると、コードが宣言的になる。
  5. 静的解析と仲直り:mypy/pyrightは文字列ベースの属性解決が苦手。TypedDictProtocolLiteral で属性名を絞る、もしくは getattr(obj, name) の直後に assert hasattr(obj, name) 相当の型絞り込みを使う。
from typing import Literal, Protocol, Callable

class HasOp(Protocol):
    def add(self, a: int, b: int) -> int: ...
    def mul(self, a: int, b: int) -> int: ...

OpName = Literal["add", "mul"]

def apply(op: OpName, a: int, b: int, impl: HasOp) -> int:
    fn: Callable[[int, int], int] = getattr(impl, op)
    return fn(a, b)

罠と注意点

  • 綴りミスが実行時まで露見しない:テスト不足だと本番で落ちる。Literal/Enum で選択肢を限定する。
  • __getattr__ が影に隠すバグ:存在しない属性にも値を返す実装だと、綴りミスが沈む。ロギングや明示的な例外にする。
  • セキュリティ__class__ やダンダー属性(__で始まる)へのアクセスは原則禁止にする。フィルタを先に。
  • パフォーマンス微妙差:ループの内側で頻繁に解決するなら、事前にローカルに束縛するか、attrgetter を一度だけ作って再利用する。
from operator import attrgetter

getter = attrgetter("score")
total = 0
for user in users:   # 大量反復を想定
    total += getter(user)  # 繰り返しの名前解決を回避

hasattr と例外設計

hasattr(obj, name) は内部的に getattr を使い、AttributeError を握りつぶして False を返す。ただし、属性取得時に別の例外が起きてもFalseになる罠がある(プロパティでDBアクセスして例外、など)。安全に判定したい場合は、getattr(obj, name, sentinel) で番兵値(sentinel)を使い、同値比較で判断するほうが透明だ。

_sentinel = object()
value = getattr(obj, "price", _sentinel)
if value is _sentinel:
    ...  # 未定義
else:
    ...  # 正常値

ダイナミックなAPI設計の型付け

属性名を公開インターフェースの一部にするなら、Enum/Literal で表現してドキュメント化しよう。IDE補完とバリデーションが効く。さらに、Protocol で実装側の最小要件を固定すると、交換可能な部品として安全に差し替えられる。

from enum import Enum
class Op(Enum):
    ADD = "add"
    MUL = "mul"
def run(op: Op, a: int, b: int, target):
    fn = getattr(target, op.value)
    return fn(a, b)

メタプログラミングとの接続

getattrsetattr/delattr と対で覚えると強い。属性の列挙は dir()、実体の辞書は vars()。クラス定義を動的に構築するメタクラスや、プラグイン自動登録のレジストリとも相性が良い。

関連関数 役割 典型用途
getattr 取得 名称→属性の解決、ディスパッチ
setattr 設定 モック差し替え、テスト注入
delattr 削除 実験的機能の一時無効化
dir 列挙 利用可能な属性の提示
vars 辞書 __dict__ による直アクセス

実戦レシピ集

  • 簡易ルーター:ビュー関数名をURLから解決。
  • 設定の段階的上書き:優先度順に属性を引き、最初に見つかった値を採用。
  • モデル→辞書変換:フィールド名リストを回し、getattr で値を抜く。
  • MLパイプライン:層名→処理の解決。attrgetter でスコア集計。
def first_found(obj, *names, default=None):
    for n in names:
        v = getattr(obj, n, None)
        if v is not None:
            return v
    return default

まとめ

getattr() は「名前で行う属性アクセス」を正道で提供する。ドットアクセスの可読性と動的性の両方を保ちつつ、既定値、attrgetter、型付けの工夫を合わせれば、運用と安全性を両立できる。過度に魔術化せず、公開インターフェースとして“選べる名前”を明示し、テストで補強するのがプロダクション品質への近道だ。最後の実務チェックリスト: ①外部入力は必ず許可リストで検証、②番兵値で未定義を判定、③大量反復ではattrgetterを再利用。④型はLiteral/Enumで絞り、Protocolで互換を担保。推奨。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?