Pythonの__dict__を徹底解説! オブジェクトの属性にアクセスする強力な方法


Pythonプログラミングにおいて、クラスやオブジェクトの属性(変数)を扱うことは日常茶飯事ですよね。しかし、その属性が内部的にどのように保持されているか、そしてそれに直接アクセスする方法があるのをご存知でしょうか? その鍵となるのが、特殊属性(Special Attribute)の1つである**__dict__**です。

この記事では、__dict__属性の基本的な概念から、その使い方、そしてPythonのオブジェクト指向プログラミングにおける役割まで、初心者の方にも分かりやすく徹底的に解説します。__dict__を理解すれば、Pythonのオブジェクトの内部構造がよりクリアになり、デバッグや動的なプログラミングの幅が広がるでしょう!

__dict__属性とは? なぜ使うのか?

__dict__は、Pythonのオブジェクトが持つ特殊属性の1つで、そのオブジェクトのインスタンス辞書を指します。この辞書には、インスタンスに固有の全ての属性がキーと値のペアとして格納されています。

なぜ__dict__を使うのでしょうか?

  • 属性の動的な操作: 実行時にオブジェクトの属性を追加、変更、削除する際に、直接この辞書を操作できます。

  • デバッグと検査: オブジェクトがどのような属性を実際に持っているかを視覚的に確認する際に非常に役立ちます。

  • メタプログラミング: プログラムが自身の構造を変更したり、他のプログラムを生成したりするような高度なテクニック(メタプログラミング)で利用されることがあります。


__dict__属性の基本的な使い方

__dict__は、オブジェクトにドット(.)で続けてアクセスします。戻り値は辞書です。

構文

Python
 
object.__dict__

戻り値

指定されたオブジェクトのインスタンス属性を格納した辞書(dictオブジェクト)

具体例:インスタンス属性の確認

Python
 
class MyClass:
    def __init__(self, name, value):
        self.name = name
        self.value = value
        self.category = "default" # 別の属性を追加

    def display(self):
        print(f"Name: {self.name}, Value: {self.value}")

obj = MyClass("Example", 123)

# __dict__属性を使ってインスタンスの属性を確認
print(obj.__dict__)
# 出力: {'name': 'Example', 'value': 123, 'category': 'default'}

このように、obj.__dict__をプリントすると、objインスタンスが持つname, value, categoryという属性が、それぞれキーと値のペアとして辞書の形式で表示されます。


__dict__属性の動作と注意点

1. インスタンス属性のみを含む

__dict__は、あくまでインスタンス固有の属性(つまり、self.attribute = valueのようにインスタンスに直接設定された属性)のみを格納します。クラス属性やメソッドは含まれません。

Python
 
class MyClass:
    class_attr = "I'm a class attribute" # クラス属性

    def __init__(self, name):
        self.name = name # インスタンス属性

    def instance_method(self): # メソッド
        print("Instance method called.")

obj = MyClass("Test")

print(obj.__dict__)         # 出力: {'name': 'Test'}
print(MyClass.__dict__.keys()) # クラスの__dict__にはメソッドやクラス属性も含まれる
# 出力例: dict_keys(['__module__', 'class_attr', '__init__', 'instance_method', ...])

クラス自体の__dict__(例: MyClass.__dict__)は、そのクラスで定義されたメソッドやクラス属性を保持します。これは、クラスがtypeというメタクラスのインスタンスであり、typeが持つ__dict__が異なる振る舞いをするためです。

2. 属性へのアクセスと変更

__dict__は通常の辞書と同様に操作できます。

Python
 
obj = MyClass("Initial", 0)
print(obj.__dict__) # {'name': 'Initial', 'value': 0, 'category': 'default'}

# 属性の追加
obj.__dict__['new_attr'] = 456
print(obj.new_attr) # 456

# 属性の変更
obj.__dict__['name'] = "Updated Name"
print(obj.name)     # Updated Name

# 属性の削除
del obj.__dict__['category']
# print(obj.category) # AttributeError: 'MyClass' object has no attribute 'category'

このようにobj.attributeのようなドット記法でアクセスする代わりに、obj.__dict__['attribute']のように辞書としてアクセス・操作できます。Pythonの内部では、obj.attributeとアクセスされた際に、まずobj.__dict__からattributeを探しに行きます。

3. __slots__との関係

もしクラスで**__slots__**を定義している場合、そのクラスのインスタンスは__dict__属性を持ちません(または非常に限定的な形式になります)。__slots__は、メモリ使用量を削減し、属性アクセスを高速化するために使用されますが、その代償としてインスタンスが動的に新しい属性を持てなくなります。

Python
 
class SlottedClass:
    __slots__ = ('x', 'y') # xとy以外の属性は持てない

    def __init__(self, x, y):
        self.x = x
        self.y = y

s_obj = SlottedClass(1, 2)
# print(s_obj.__dict__) # AttributeError: 'SlottedClass' object has no attribute '__dict__'

__slots__を使用する際は、__dict__への依存性を考慮する必要があります。

4. dir()vars()との比較

  • __dict__: インスタンスに固有の属性を辞書として返します。

  • dir(object): オブジェクトがアクセスできる全ての属性とメソッド名のリストを返します(インスタンス属性、クラス属性、メソッド、継承された属性など)。

  • vars(object): object.__dict__と同じものを返します。引数を省略すると現在のスコープのローカル変数を辞書で返します。__slots__を持つオブジェクトには使えません。

Python
 
obj = MyClass("Test", 100)
print(obj.__dict__) # インスタンス属性のみ
print(vars(obj))    # __dict__と同じ
print(dir(obj))     # インスタンス属性、クラス属性、メソッドなど全て

__dict__属性の活用場面

1. デバッグやオブジェクトの検査

実行中のオブジェクトがどのような状態にあるかを素早く確認したい場合に非常に役立ちます。

Python
 
class User:
    def __init__(self, user_id, name, email=None):
        self.user_id = user_id
        self.name = name
        self.email = email

user1 = User(101, "Alice", "alice@example.com")
user2 = User(102, "Bob")

print("User1の属性:")
for key, value in user1.__dict__.items():
    print(f"  {key}: {value}")

print("\nUser2の属性:")
for key, value in user2.__dict__.items():
    print(f"  {key}: {value}")

2. オブジェクトの動的なシリアライズ/デシリアライズ

オブジェクトの属性を辞書として取得できるため、JSONなどの形式にシリアライズ(データ変換)したり、辞書からオブジェクトを再構築(デシリアライズ)したりする際に便利です。

Python
 
import json

class Config:
    def __init__(self, host, port, debug_mode):
        self.host = host
        self.port = port
        self.debug_mode = debug_mode

my_config = Config("localhost", 8000, True)

# オブジェクトを辞書に変換し、JSONとして保存
config_dict = my_config.__dict__
json_string = json.dumps(config_dict, indent=2)
print(json_string)

# JSONからオブジェクトを再構築 (シンプルな例)
# reloaded_config_dict = json.loads(json_string)
# reloaded_config = Config(**reloaded_config_dict) # 辞書を展開して引数に渡す
# print(reloaded_config.host)

3. メタクラスや高度なフレームワーク開発

通常のアプリケーション開発で__dict__を直接操作することは稀ですが、DjangoのORMやSQLAlchemyのような高度なフレームワークでは、オブジェクトの属性を動的に操作するために__dict__が内部的に利用されることがあります。


まとめ

__dict__属性は、Pythonオブジェクトのインスタンス属性がどのように格納されているかを示す強力な窓口です。

  • オブジェクトのインスタンス属性を辞書形式で保持する。

  • 属性の動的な追加、変更、削除に利用できる。

  • デバッグやオブジェクトの内部状態の検査に非常に有効。

  • クラス属性やメソッドは含まない

  • __slots__を持つクラスのインスタンスは通常__dict__を持たない。

__dict__を理解することは、Pythonのオブジェクトモデルをより深く理解し、必要に応じて高度なテクニックを適用するための基盤となります。ぜひ今日学んだことを、あなたのコーディングに活かしてみてくださいね!


らくらくPython塾 – 読むだけでマスター

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座