読みやすいPythonコードの書き方 (Ruff・命名規則・型ヒント) - 人工知能応用特論Ⅰ 第6回

18.7K Views

November 11, 25

スライド概要

東京農工大学大学院先進学際科学府の人工知能応用特論Ⅰの第6回資料です.

profile-image

東京農工大学工学部知能情報システム工学科・先進学際科学府 准教授

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

2025年度 人工知能応用特論Ⅰ: 機械学習・データサイエンスのための プログラミング 第6回: 読みやすいPythonコードの書き方 東京農工大学大学院先進学際科学府 山田宏樹 1

2.

スケジュール 第1回:Gitの基礎 第2回:Git・GitHubの実践 第3回:Pythonの環境構築 第4回:Dev Containerによる開発環境の構築 第5回:Visual Studio Code (VS Code) を活用した開発 第6回:読みやすいPythonコードの書き方 第7回:pytestを使ったテスト駆動開発 第8回:機械学習のプロジェクトにおける実験管理 スケジュールはあくまで暫定的なものです 2

3.

今回の授業の目標 Pythonコードの可読性を担保する方法を学ぶ プログラミングにおいてコードの可読性を担保するのは非常に重要です.「自分し かコードを読まないから適当でいいや」などと思ってはダメです.1週間前の自分 はほぼ他人です.自分で書いたコードであっても可読性が低いとスムーズに開発 を再開することができなくなります.では,どうやって可読性を担保すれば良い のでしょうか?本講義ではその方法を解説します. 3

4.

実行環境 今回の講義で使うツールは以下の通り Visual Studio Code (VS Code) uv Ruff 講義の課題で,上記のインストールは完了していると思います. 4

5.

目次 1. Pythonのコーディング規約と Ruff PEP 8のPythonコーディング規約 Ruff による静的解析・自動整形 2. プログラムの命名の重要性 命名のアンチパターン どのような名前をつけるべきか? 3. 型ヒントを使う デバッグの流れ Pythonデバッグの設定 デバッグの演習 5

6.

1. Pythonのコーディング規約と Ruff 6

7.

PEP (Python Enhancement Proposals) とは? PEPとはPythonの公式な提案書のことです.全てのPEPには一意の番号がつけられ ており,PEPそのものについての説明はPEP 1に記載されています. PEP 1 より引用 (PEP 1 – PEP Purpose and Guidelines) What is PEP? PEP stands for Python Enhancement Proposal. A PEP is a design document providing information to the Python community, or describing a new feature for Python or its processes or environment. The PEP should provide a concise technical specification of the feature and a rationale for the feature. 7

8.

PEP 20: The Zen of Python Pythonの設計思想を表した格言が記載されています. Pythonで import this と入力すると呼び出すことができます. Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. ... 8

9.

PEP 8: Pythonコードのスタイルガイド PEP 8にはPythonのコーディング規約(スタイルガイド)が記載されています.コ ードの一貫性を保つために様々なルールが記載されています. 代表的なルール インデントはスペース4つに統一,インデントにタブを混ぜない 標準ライブラリ,サードパーティ,自作ライブラリの順でimportする 命名規則 (これは後で解説) 空行や空白をどこで入れるか?,またどこで改行を入れるべきか? など,様々なルールが存在します.PEP 8に準拠したコーディングをしましょう. PEP 8に違反して良いのは,明確な理由がある場合のみです. 9

10.

PEP8の命名規則 パッケージ・モジュール snake_case方式,つまりスペースをアンダースコア _ で置き換え単語は全て小文 字で書く表記方法を使います.(例: my_module ) クラス CapWords方式,つまりスペースを使わず各単語の最初の文字を大文字で書く表記 方法を使います.(例: MyClass ) 関数・メソッド snake_case方式を使います.(例: my_function ) 変数・定数 変数の場合は,snake_case方式を使います.(例: my_variable ).定数の場合は UPPER_CASE方式,つまりスペースをアンダースコア _ で置き換え単語を全て大 文字で書く表記方法を使います.(例: MY_CONSTANT ). 10

11.

Ruff を使ってPEP 8に準拠したコードを書く PEP 8の規約を熟知し,手作業で常にその規約を守ったコードを書くのは少し現 実的ではないかもしれません.そこで,コーディング規約を自動でチェックし整 形してくれるツールである Ruff を活用しましょう! Ruffとは? (Ruff公式サイト) Astral社 ( uv の会社と一緒) が開発している,Rustで書かれている高速なPythonコ ードのリンター・フォーマッターツールです. リンター (Linter): ソースコードを分析して、潜在的なバグ、構文エラー、コーデ ィングスタイルの違反などを指摘してくれる「静的解析ツール」のこと. ( Flake8 や Pylint などが有名) フォーマッター (Formatter): コードを「自動整形」するツール.(代表例: Black や isort などが有名) 従来は Flake8 + black + isort などを組み合わせて静的解析と自動整形をしてい ましたが, Ruff だけで代替できるようになりました. 11

12.

演習: Ruff のチュートリアル Ruffの公式サイトにあるチュートリアルを一緒にやっていきましょう! Getting Started uv で numbers というプロジェクトを作る みなさんは uv をすでにインストール済みだと思います. uv では --lib のオプシ ョンをつけるとsrcレイアウトでプロジェクトが作られます. uv init --lib numbers 12

13.

次に, src/numbers/calculate.py を作ります. from typing import Iterable import os def sum_even_numbers(numbers: Iterable[int]) -> int: """Given an iterable of integers, return the sum of all even numbers in the iterable.""" return sum( num for num in numbers if num % 2 == 0 ) Ruff を uv でインストールする では --dev をつけると開発用の依存パッケージとしてインストールすること ができます. uv uv add --dev ruff 13

14.

uv で Ruff を実行してみよう を使って Ruff を実行します. Ruff の静的解析を実行するときは ruff check を使います. uv run uv run ruff check 実行結果 yamadakoki@MacBook-Pro-2021 numbers % uv run ruff check F401 [*] `os` imported but unused --> src/numbers/calculate.py:3:8 | 1 | from typing import Iterable 2 | 3 | import os | ^^ | help: Remove unused import: `os` Found 1 error. [*] 1 fixable with the `--fix` option. この結果は使っていない os ライブラリがあることを警告してくれています. 14

15.

"fixable"と書かれたエラーは ruff check --fix を実行すれば,自動で解決してく れます. uv run ruff check --fix 実行結果 yamadakoki@MacBook-Pro-2021 numbers % uv run ruff check --fix Found 1 error (1 fixed, 0 remaining). src/numbers/calculate.py 確認できます. をみてみると, import os が削除されていることが from typing import Iterable def sum_even_numbers(numbers: Iterable[int]) -> int: """Given an iterable of integers, return the sum of all even numbers in the iterable.""" return sum(num for num in numbers if num % 2 == 0) 15

16.

RuffのFormatterを実行する ruff format を使うと,RuffのFormatterを実行することができます. uv run ruff format 実行結果 yamadakoki@MacBook-Pro-2021 numbers % uv run ruff format 1 file reformatted, 1 file left unchanged をみてみると, from typing import Iterable 以下 の空行が3行のところが2行に自動整形されます. src/numbers/calculate.py 16

17.

Ruff の設定方法 の設定は pyproject.toml に記載します.設定の詳細はConfiguring Ruffを 確認しましょう.本講義では,設定の一例を紹介します. Ruff [tool.ruff] line-length = 110 # 1行あたりの最大文字数を指定 target-version = "py312" # 対象のPythonバージョンを指定 [tool.ruff.lint] select = [ "E", # pycodestyle errors (PEP 8のコーディング規約をチェック) "W", # pycodestyle warnings (PEP 8のコーディング規約をチェック) "F", # pyflakes (文法のエラーをチェック) "I", # isort (importの順序をチェック) "N", # pep8-naming (命名規則をチェック) "B", # flake8-bugbear (プログラム内のバグや設計上の問題を検出) "UP", # pyupgrade (Pythonの古い構文を新しい構文に自動で変換) "SIM", # flake8-simplify (冗長なコードを簡略化) ] ignore = ["F722"] # unfixable = [ "F401", # unused import "F841", # unused variable ] 17

18.

[tool.ruff.format] quote-style = "double" # 文字列を"..."で統一 docstring-code-format = true # docstring内コードも整形 tool.ruff Ruff 全体の設定をします. tool.ruff.lint リンターの設定をします. select はチェックするルールを指定します.ルールの 一覧はRulesを参照. ignore は無視したいルールを指定します. unfixable は自 動的に修正しないルールを指定します. tool.ruff.format フォーマッターの設定をします. 18

19.

Ruff をVS Codeで使う VS Codeの Ruff の拡張機能を使うと,ファイルの保存時に静的解析・自動整形を してくれます.次のように設定を行います. 1. Ruff の拡張機能をインストール 拡張機能ビューから以下の Ruff 公式の拡張機能をインストールしてください. 19

20.

2. .vscode/settings.json に Ruff の設定を追記 は現在のプロジェクト (ワークスペース) 専用のVS Code を設定するためのファイルです. .vscode/settings.json がなければ作成し,以 下の Ruff の設定を追記しましょう. .vscode/settings.json { "[python]": { "editor.formatOnSave": true, "editor.defaultFormatter": "charliermarsh.ruff", "editor.codeActionsOnSave": { "source.fixAll.ruff": "explicit", "source.organizeImports.ruff": "explicit" }, } } この設定を終えると,ファイルを保存時に自動整形できるようになります. 試しに,演習で使った calculate.py に import os と追記して保存してみてくだ さい.自動で import os が削除されると思います.また, pyproject.toml で unfixable に"F401"を追加しておけば自動で削除しなくなります. 20

21.

2. プログラムの命名の重要性 21

22.

なぜプログラムの命名が重要なのか? 「優れた」コードとは? この問いの一つの解答は,「他の人が最短時間で理解できるようなコード」です. コードの使用・機能追加するためには「理解」が必要です.他の人が理解しやす いコードを書くことを心がけましょう.そのための第一歩が,わかりやすい名前 をつけることです. 名前に情報を詰め込む 変数・関数・クラスの名前は短いコメントのようなものです.的確でわかりやす い名前をつけることで,可読性が一気に向上します.わかりやすい名前の付け方 を紹介していきます. 22

23.

命名のアンチパターンを知る わかりやすい名前をつける方法を紹介する前に,絶対にやってはいけないアンチ パターンを紹介します. 連番での命名 や func2 のように連番をつけて命名するのはやめましょう.連番は何も意 味を持ちませんし,コードを読む人が混乱します. func1 ローマ字の使用 や kenmei のように日本語をローマ字で表記するのはやめましょう.英 語で書くべきです.ローマ字で書かれていると,そのプログラムを公開したとき に非日本語話者が困ります.また,英語で書いておいた方が生成AIによるコード 補完の精度が上がります. tokucho 23

24.

意味のない短い名前 ループカウンタ以外で, i , j , k などのような意味の伝わらない変数をつけるの はやめましょう.ループカウンタであっても, i , j とせずに members_i , users_j のように工夫するとより可読性が向上します. 一般的でない略語の使用 例えば user_name を usr_nm とするといった,初見で理解できない過度な省略は やめましょう.よく知られた省略表記以外を命名に使うのは可読性を一気に下げ てしまいます.( evaluation を eval とするのは,よく知られているのでOK.迷 ったらよく知られた省略表記なのかを調べて使いましょう) 以降では,良い命名をするための方法を紹介します. 24

25.

明確な単語を選ぶ 「名前に情報を詰め込む」ためには,使う単語を吟味する必要があります. 例: 関数名の命名 プログラムの関数名は,関数が実行する「動き」を表す動詞から始めるのが基本 的な命名規則です.その動きをより明確に表す動詞を選定するのが重要です.例 えば,インターネットからページを取ってくる関数の命名であれば, get_page とするよりも fetch_page とするか download_page とする方が明確です.意味の 広い get は少し抽象的になりすぎる場合があります. 命名に迷ったときには シソーラス (類語辞典) を使う 生成AIに良い命名方法を聞く Qiita: 【日本人エンジニア必携】英語命名規則の決定版に記載されている命名 のフローチャートを使ってみる. 25

26.

名前に情報を付加する 型を明示する命名 変数名に型の情報を含めることで可読性が高くなります. Bool型の場合 , has_x , can_x , x_exists , x_enabled のようにprefix (接頭語) やsuffix (接尾語) をつけることで,変数が真偽値を持つことを明示できます. is_x リスト型の場合 複数形を用いるか, list_x のようにprefixをつけることで,リストであることを 明示できます. 辞書型の場合 や dict_<key>_<value> と命名することで,辞書であること を明示できます.例えば, token_map_id など. <key>_map_<value> 26

27.

値の単位を付加する 単に time とするのではなく, time_sec , time_ms のように単位を表すsuffixを つけましょう.研究では実際に計測したデータを扱うことも多いので単位を明記 しておいた方が良いです. 27

28.

名前の長さを決める 名前の長さは「スコープ」の広さに応じて決めよう スコープ (その名前が見えるコードの行数)が小さければ,短い変数名でも可読性 は低下しません.一方で,スコープの大きい変数には情報を詰め込んで明確にす る必要があります. 長い名前を入力するのを過度に避けない アンチパターンでも触れましたが,変数名を短くしようとして一般的でない省略 表記を使うのはやめましょう.また変数名の長さを理由に必要な情報を入れるの を避けるのもやめましょう.テキストエディタには単語補完の機能があるので, 長い名前を入力するのにさほどの手間はかかりません.VS Codeでは途中まで変 数名を入力すると単語補完が効いて候補をリストアップしてくれます. 28

29.

3. 型ヒントを使う 29

30.

静的型付け言語と動的型付け言語 静的型付け言語 (C, C++, Java, Rustなど) 静的型付けではコンパイル時に変数に型情報を埋め込みます.変数は実行時にデ ータ型が変化しないため,頑健なコードを書くことができます. 動的型付け言語 (Python, JavaScript, Ruby, PHPなど) 動的型付けでは値自体に型情報を埋め込みます.変数に固定された型情報はない ので,実行時に型を変更することができます.コードを書く際に型を気にする必 要がないので,扱いやすい言語となっています.一方で,型を明示的に指定しな いので,変数がどんな値を想定しているのかがわかりにくくなったり,型の不一 致や意図しない型変換に関するエラーの解決に時間が取られるといったデメリッ トもあります. Pythonでは型ヒントを使うことで,動的型付けの扱いやすさを担保しつ つ,型情報を明示することができます. 30

31.

型ヒントとは? 型ヒント (Type Hints) は変数のデータ型として期待するものを利用者に示すため の構文です.Python 3.5で型ヒントが導入されており, typing モジュールをイン ポートして型に関する情報を付加することができます.また型ヒントの仕様は PEP 484 に記載されています. 型ヒントを導入するメリット コードの可読性・保守性の向上: 他の人がコードを読むときに,その変数のデ ータ型を想定することが可能となる. 静的型チェッカーやIDEによるエラーの早期発見: mypy や Pyright などの静 的型チェッカーを使うことで,型の不整合を検知できる. コード補完機能の向上: GitHub CopilotなどのAIを使った補完の精度が向上 ただし,型ヒントは実行時にエラーを出さないことに注意してください.あくま で「ヒント」であって「制約」ではありません.静的型チェッカーを使ったとき に警告やエラーを出してくれます. 31

32.

型ヒントの使い方 型ヒントをつけるのは簡単です.基本的には,変数名の後に : をつけ,型の名 前を記載するだけです.関数の戻り値に型ヒントをつけたい場合は,関数の定義 の後ろに -> をつけて想定する戻り値の型名を記載すれば良いです. def divide(a: int, b: int) -> float: """a を b で割った結果を返す""" return a / b def total_length(names: list[str]) -> int: """文字列リストの長さの合計を返す""" return sum(len(n) for n in names) numpy も型ヒントに対応しています. import numpy as np import numpy.typing as npt def normalize(x: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: """ベクトルを正規化する関数""" norm = np.linalg.norm(x) return x / norm 32

33.

(補足) Python 3.8以前の型ヒント Python 3.8以前は typing モジュールのジェネリック型を使う必要がありました. from typing import List, Dict, Tuple def process_data( items: List[int], config: Dict[str, str], point: Tuple[float, float], ) -> List[str]: return [str(x) for x in items]** Python 3.9以降はビルトイン型(list, dict, tuple, setなど)が typing モジュールを インポートすることなく型ヒントをつけられるようになりました. # --- 新しい書き方(Python 3.9以降) --def process_data( items: list[int], config: dict[str, str], point: tuple[float, float], ) -> list[str]: return [str(x) for x in items] 33

34.
[beta]
さまざまな型ヒント
Optional

型

型は「値が None になる可能性がある」ことを明示する型ヒントです.
言い換えれば,変数に値をセットするかどうかがオプションとなっています.関
数の引数が省略可能な場合に使うと便利です.
Optional

from typing import Optional

# Python 3.9以前向け

def greet(name: Optional[str]) -> str:
"""名前が None の場合は 'Guest' として挨拶する"""
if name is None:
return "Hello, Guest!"
return f"Hello, {name}!"

Python 3.10 以降は X | None の書き方も使えます.
# Python 3.10以降の新しい書き方
def greet(name: str | None) -> str:
if name is None:
return "Hello, Guest!"
return f"Hello, {name}!"

34

35.
[beta]
Union
Union

型

型は 「値が複数の型のいずれかである」ことを表す型ヒントです.

from typing import Union
def process(value: Union[int, str]) -> str:
"""整数または文字列を受け取り、文字列として返す"""
if isinstance(value, int):
return f"Number: {value}"
return f"Text: {value}"

Python 3.10 以降は | の書き方も使えます.
def process(value: int | str) -> str:
if isinstance(value, int):
return f"Number: {value}"
return f"Text: {value}"

もちろん,3つ以上の型も指定できます.(例: int | float | None )

35

36.
[beta]
Literal
Literal

型

型は特定の定数値しか受け付けないことを表す型ヒントです.

from typing import Literal
def set_mode(mode: Literal["train", "test"]) -> None:
"""モードを train または test に限定する"""
print(f"Mode is {mode}")

Any

型

型は型チェックを無効化したい場合や,任意の型を受け入れたい場合に使う
型ヒントです. Any を使うと型チェッカーはその変数に関して何も警告しないこ
とに注意しましょう.
Any

from typing import Any
def debug(value: Any) -> None:
print(value)

36

37.

Callable Callable 型 は「関数や呼び出し可能オブジェクト」を表すための型ヒントです. from typing import Callable def apply(func: Callable[[int, int], int], a: int, b: int) -> int: return func(a, b) def add(x: int, y: int) -> int: return x + y def multiply(x: int, y: int) -> int: return x * y print(apply(add, 2, 3)) print(apply(multiply, 2, 3)) # 5 # 6 これ以上は取り上げませんが,他にも様々な型ヒントが存在します. 型ヒントの情報は typing --- Support for type hints に記載があります. 自分がつけたい型ヒントをどうやって実現すれば良いか迷ったら,生成AIに聞い てみると良いです.自分の知らない型ヒントが見つかると思います. 37

38.

型ヒントの実践的な導入 すでに大規模なコードがある場合,型ヒントをつけていくのは大変だと思いま す.その場合は,型ヒントを追加する箇所を戦略的に限定するのが良いです.以 下は型ヒントをつけるルールの一例です. これから新しく作る関数やクラスの引数,戻り値には全て型ヒントをつける これから書き換える古いコードに型ヒントをつける 複雑な挙動をする部分に型ヒントをつける とりあえず,これから新しく作る関数やクラスの引数,戻り値には全て型ヒント をつけることを強く推奨します. 38

39.

本講義で扱えなかったこと dataclassとpydanticについて 構造化されたデータを扱う場合には,辞書ではなく dataclass を使うことで,可 読性・型安全性を高めることができます.興味のある方は,以下の文献・記事を 参考にしましょう. P. Viafare, ロバストPython, OREILLY: 型ヒントの使い方,型チェカーの使い方 など,保守しやすいコードの書き方がまとまっています.また, dataclass や pydantic についても詳しく解説されています. dataclass で万物に型を付けよう: Pythonで学ぶ画像生成という本のコラム記 事です. dataclass の実践的な使い方が紹介されています. jaxtypingを使った型ヒントについて は型ヒントで数値配列(特にNumPy / JAX / PyTorch / TensorFlowな ど)を扱うときの形状やデータ型まで静的にチェックできるようにするライブラ リです.配列の形状まで明示的に書けるようになるので,おすすめです. (参考:pytorchのdtype, shapeを型安全にするjaxtypingのすすめ) 39 jaxtyping

40.

参考資料 コードの可読性を上げるのは一朝一夕には実現できません.今回紹介したのは, すぐにできる対応策のみです.より読みやすいコードを書くためには以下の参考 文献を読みましょう. 浅野,先輩データサイエンティストからの指南書,技術評論社 Pythonの環境構築の話から,コードやデータの品質管理,実験管理について学べ ます.データサイエンスに取り組む人は,この本に記載されている内容は全て押 さえておくと良いでしょう. D. Boswell, リーダブルコード, OREILLY 言わずと知れた名著です.とりあえず第I部だけでも読んでおくと良いです. 仙塲, 改訂新版 良いコード/悪いコードで学ぶ設計入門, 技術評論社 より良いコードの書き方・設計について学べます.プログラム始めたてだと少し 難しいかもしれまんが,すごく良い本なのでおすすめです. 40

41.

参考になる記事 Pythonのコードを美しく保つには: Pythonで学ぶ画像生成という本のコラム 記事です. Ruff の実践的な使い方が紹介されています. すべてを救う Python の型ヒントについて: こちらもPythonで学ぶ画像生成と いう本のコラム記事です.型ヒントを導入するメリットや使用例がまとまっ ています. 41

42.

課題 課題内容 本講義で扱っていない型ヒントを一つを調べ,使用例をまとめてください.ファ イル名は 学籍番号_名前_typehint としてください.また,ファイル形式は pdf に してください. 注意事項 ファイル名と拡張子が正しいか確認すること. 締め切り 日本標準時で次回講義の前日の23:59まで 42

43.

ここまで資料をご覧になっていただきありがとうございます. 資料に間違いがあったり,より良いベストプラクティスがある 場合はご連絡ください.次回以降の講義にフィードバックを活 かします. Twitter: @KokiYamada6 本講義は,学習コストと得られる恩恵のバランスをとっています.より良いベス トプラクティスがあったとしても,学習コストの理由により採用しない場合があ ります.その点についてご了承ください. 43