Pythonで変数のdocstringを書く（VSCode）

ふつうは関数とか`class`とかに書くdocstringですが、実は特定の場所の変数にもdocstringを書くことができます。
- モジュール トップレベルの `global` 変数
- `class` 内で代入した変数
- `__init__` 内で代入した変数

しかもなんと、VSCodeでは変数名にマウスオーバーすると **変数の説明をホバー表示** させることができます。
めちゃくちゃ便利！！

この変数docstringは [PEP 257](https://peps.python.org/pep-0257/) で定義されている仕様らしく、メジャーなIDE等であればだいたい対応しているはずです。

>String literals occurring elsewhere in Python code may also act as documentation. They are not recognized by the Python bytecode compiler and are not accessible as runtime object attributes (i.e. not assigned to `__doc__`), but two types of extra docstrings may be extracted by software tools:
>1. String literals occurring immediately after a simple assignment at the top level of a module, class, or `__init__` method are called “attribute docstrings”.
>2. String literals occurring immediately after another docstring are called “additional docstrings”.

<details>
<summary>日本語訳（訳：ChatGPT）</summary>

Pythonコード内のその他の場所に現れる文字列リテラルも、ドキュメンテーションとして機能する場合がある。これらはPythonのバイトコードコンパイラによって認識されず、実行時オブジェクトの属性（すなわち `__doc__`）としてはアクセスできない。しかし、以下の2種類の追加的なdocstringはソフトウェアツールによって抽出される可能性がある。

1. モジュール、クラス、または `__init__` メソッドのトップレベルにおいて、単純な代入の直後に現れる文字列リテラルは、「属性docstring（attribute docstrings）」と呼ばれる。
2. 他のdocstringの直後に現れる文字列リテラルは、「追加docstring（additional docstrings）」と呼ばれる。

</details>


# 環境
- Python 3.11.9
- VSCode 1.96.4
  - Pylance 2024.12.1


# まず、普通の書き方
こんな感じのPythonスクリプトがあるとします。
今適当に書いた、ふっつーのコードです。

`def output_test_txt() -> None:` の下にある `"""テストファイルを一定量出力する関数"""` がいつものdocstringですね。
```py
from pathlib import Path

# test.txt のパス
TEST_TXT_PATH = Path("D:/test.txt")

# 出力する最大の行数
OUTPUT_MAX_COUNT = 50


def output_test_txt() -> None:
    """テストファイルを一定量出力する関数"""

    with open(TEST_TXT_PATH) as f:
        lines = f.readlines()

    output_len = min(len(lines), OUTPUT_MAX_COUNT)
    for i in range(output_len):
        print(f"{i+1}: {lines[i]}")


if __name__ == "__main__":
    output_test_txt()
```
定数 `TEST_TXT_PATH`, `OUTPUT_MAX_COUNT` をグローバル変数として定義してあります。

ここで、関数の中で定数 `OUTPUT_MAX_COUNT` がどういう目的の定数なのか忘却したとします。

なんだっけこの定数…。出力最大数…？文字数？行数？

そこで、マウスオーバーしてみますが…
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652113/84da366d-014f-c599-8ef7-7d85876371fa.png)

ホバー（ツールチップ）に説明が出てこない！

関数ならdocstringが表示されるのに…
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652113/7099d7b5-2ea1-8667-84b1-9d070e823d18.png)


F12を押して定義した行にジャンプすれば済む話ではありますが、ホバー表示させられるならしたいですよね。
関数は出るんですし…


# docstring付きの変数の書き方
以下のように変数の下にdocstringを書くことで、VSCodeが変数とdocstringを結び付けてくれます。
（注：特定の変数にしか結び付けられません。後述の注意点②を参照）
```py
from pathlib import Path


TEST_TXT_PATH = Path("D:/test.txt")
"""test.txt のパス"""

OUTPUT_MAX_COUNT = 50
"""出力する最大の行数"""


def output_test_txt() -> None:
    """テストファイルを一定量出力する関数"""

    with open(TEST_TXT_PATH) as f:
        lines = f.readlines()

    output_len = min(len(lines), OUTPUT_MAX_COUNT)
    for i in range(output_len):
        print(f"{i+1}: {lines[i]}")


if __name__ == "__main__":
    output_test_txt()
```
変数の下にdocstringを書くのがミソ！
関数なんかと同じ書き方ですね。

この状態で、関数内にある定数の参照にマウスオーバーすると…
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652113/53112c98-071e-bd82-ed09-7a07fa7d06a9.png)

出た～！！

これは…便利！！
変数名を変えるときもdocstringを読みながら入力できちゃう！
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652113/b7b2a7b7-b1ca-20df-c257-f43d8cb59711.png)


## 注意点①：書く場所に注意
docstringは **変数の定義よりも後** に書きます。

たとえば、以下のように普通のコメントっぽく書いてしまうと…
```py
"""test.txt のパス"""
TEST_TXT_PATH = Path("D:/test.txt")

"""出力する最大の行数"""
OUTPUT_MAX_COUNT = 50
```

このように、上で定義した変数と関連付けされてしまいます。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652113/2eac1cd0-72b4-7796-4e49-2b4c44e2ac11.png)
そっちじゃない！


## 注意点②：ただの変数にはdocstringを書いても反応しない
冒頭にも書きましたが、docstringを書ける！と [PEP 257](https://peps.python.org/pep-0257/) で定義されている変数は以下の3種類のみです。

- モジュール トップレベルの `global` 変数
- `class` 内で代入した変数
- `__init__` 内で代入した変数

いずれも、定義とは離れた場所からdocstringを参照したい変数ですね。

通常の変数（関数内の `local` 変数とか）はdocstringっぽいものを書いてもホバー上には表示されないので注意。
説明的な変数名にするとか変数を長く使いまわさないとか、そんな感じの書き方の工夫でなんとかしてください。


# まとめ
定義した場所とは離れた場所で使う変数は、関数などと同じように変数の下にdocstringを追加しとくことで読みやすいコードになります。
絶対に良い習慣だと思うので、ぜひぜひ今から変数docstring書き始めてください！

コメントが下に来るのだけちょっとキモいですが、そういえば関数docstringとか条件式 `～ if condition else ～` に初遭遇したときもだいぶキモかったので結局は慣れだと思います。