【Python初心者】Pythonの“ダンダ（__〇〇__）”をわかりやすく解説

Pythonを学んでいると、`__init__` や `__name__` のように「アンダースコアが2つずつついた名前（`__〇〇__`）」に出会います。  
これらは「ダンダ（Dunder）」とも呼ばれ、Pythonにおいて特別な意味を持つマジックメソッドや特殊変数です。

最初はこんなふうに思ったことはありませんか？

- なんでアンダースコアが2つも？
- これはいつ使うの？

本記事では、Pythonで頻出する“ダンダ（`__〇〇__`）”について、わかりやすく解説します。

## ダンダ（`__〇〇__`）とは？

「ダンダ（dunder）」は “double underscore” の略語で、  
`__name__` や `__init__` のように「前後をアンダースコア2つで囲まれた名前」のことを指します。

Pythonでは、この形式の名前は**特別な意味や役割**を持ち、主に以下の3種類に分類できます：

1. **マジックメソッド（特殊メソッド）**  
　　例：`__init__`, `__str__`, `__len__`, `__getitem__`

2. **特殊変数（ビルトイン変数）**  
　　例：`__name__`, `__file__`, `__doc__`

3. **その他（構文では使わない内部的な名前）**  
　　例：`__pycache__`, `__init__.py`, `__main__`（後述）

---

## 1. よく使うマジックメソッド（特殊メソッド）

### ・ `__init__` ：インスタンスの初期化（コンストラクタ）

クラスからインスタンスを生成するときに自動で呼ばれるメソッドです。  
主にインスタンス変数の初期化に使われます。

    class User:
        def __init__(self, name):
            self.name = name

        def say_hello(self):
            return f"Hello, {self.name}"

    user = User("Taro")
    print(user.say_hello())  # Hello, Taro

### ・ `__str__` / `__repr__` ：文字列表現の制御

`print()` 関数などでオブジェクトを表示したときに、どのように文字列として出力されるかを定義できます。  
`__str__` は人間向け、`__repr__` は開発者向けの表現に使われることが多いです。

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

        def __str__(self):
            return f"Point({self.x}, {self.y})"

    p = Point(1, 2)
    print(p)  # Point(1, 2)

### ・ `__len__` ：長さの取得

組み込み関数 `len()` を呼んだときに実行されるメソッドです。  
リストのような「長さを持つクラス」にこのメソッドを実装することで、`len(obj)` が使えるようになります。

    class MyList:
        def __init__(self, data):
            self.data = data

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

    items = MyList([1, 2, 3])
    print(len(items))  # 3

### ・ `__getitem__` / `__setitem__` ：インデックスアクセス

リストや辞書のように `obj[key]` 形式で要素を取得・代入できるようにするメソッドです。  
カスタムクラスにリスト的な動作を持たせたいときに使われます。

    class MyDict:
        def __init__(self):
            self.store = {}

        def __getitem__(self, key):
            return self.store.get(key, None)

        def __setitem__(self, key, value):
            self.store[key] = value

    d = MyDict()
    d["a"] = 100
    print(d["a"])  # 100



## 2. よく使う特殊変数（ビルトイン変数）

### ・ `__name__` ：現在のスクリプトの実行モードを示す

スクリプトが「直接実行された」のか「importされた」のかを判定するための変数です。  
直接実行時は `"__main__"` が入ります。

#### 例1：2ファイル構成で確認

    # submodule.py
    print("__name__ in submodule.py:", __name__)

    # main.py
    import submodule

    print("__name__ in main.py:", __name__)

#### 実行結果（main.pyを実行）：

    __name__ in submodule.py: submodule  
    __name__ in main.py: __main__

#### 例2：`__name__ == "__main__"` の使い方

    def main():
        print("これはメイン処理です")

    if __name__ == "__main__":
        main()

### ・ `__file__` ：ファイルのパス情報を持つ

スクリプトファイルの絶対パス（または相対パス）を表す変数です。  
デバッグや設定ファイルの読込パスを取得するときに使われます。

    print(__file__)  # 現在のファイルの絶対パス

### ・ `__doc__` ：ドキュメンテーション文字列

関数やクラスに記述されたdocstring（説明文）を取得するための変数です。  
`help()` 関数や開発ドキュメントにも使われます。

    def greet():
        """挨拶をする関数"""
        return "Hello"

    print(greet.__doc__)  # 挨拶をする関数



## 3. その他

### ・ `__main__` とは？

`__main__` は、`__name__` に格納される文字列で、「このファイルが直接実行された」ことを意味します。  
`__name__ == "__main__"` の形で実行判定に使われるのが一般的です。

    if __name__ == "__main__":
        main()

### ・ `__pycache__` は構文上のダンダではない

`__pycache__` は、Pythonが `.py` ファイルを実行したときに自動生成するキャッシュ用フォルダです。  
コード内で使用することはなく、実行速度向上のために `.pyc` ファイルが保存されます。

### ・ `__init__.py` とは？

`__init__.py` は、ディレクトリを「Pythonパッケージ」として認識させるための初期化ファイルです。  
中身は空でもよく、共通関数や初期化処理を書くこともできます。

#### 例：

    mypackage/
    ├── __init__.py
    └── module.py

    # 呼び出し側
    import mypackage.module

---

## まとめ

- `__〇〇__` は「ダンダ（dunder）」と呼ばれ、Pythonにおいて特別な意味を持つ名前  
- ダンダには主に3種類ある：
    1. マジックメソッド（例：`__init__`, `__str__`）  
    2. 特殊変数（例：`__name__`, `__file__`, `__doc__`）  
    3. その他（構文では使わない内部名：`__pycache__`, `__init__.py`, `__main__`）

- `__name__ == "__main__"` はスクリプトの実行制御に使える重要な仕組み  
- 見た目が似ていても、使い方や意味が全く異なるものがあるので注意！

最初はちょっととっつきにくいかもしれませんが、
実際にコードを書きながら出会ったタイミングで「これはどういう意味だったかな？」と立ち返ることで、少しずつ身についていくと感じました。
ダンダ系の要素は奥が深いですが、ひとつずつ整理しながら理解していきたいと思います。