この単位では、Pythonらしい簡潔な書き方と定番のコレクション処理を扱う。
- リスト内包表記
- 辞書内包表記
- 集合内包表記
- ジェネレータ式
enumeratezipsortedreversedanyall- パック / アンパック
- 複数代入
- ラムダ式
この単位の主な論点は次の通り。
- 内包表記を使うと、コレクション生成を簡潔に書ける
- リスト内包表記は、変換や絞り込みを1行にまとめられる
- 辞書内包表記は、キーと値を組み立てながら
dictを作れる - 集合内包表記は、重複しない値の集まりを作れる
- ジェネレータ式は、必要なタイミングで値を取り出す
enumerateは、インデックスと要素を同時に取り出せるzipは、複数のコレクションを同時に処理できるsortedとreversedは、並び順を扱うときに使うanyとallは、条件を満たす値の有無や全体判定に使う- パック / アンパックを使うと、複数の値をまとめたり分解したりできる
- ラムダ式は、短い無名関数をその場で渡したいときに使う
この単位のファイル構成は次の通り。
src/06_pythonic_constructs/
main.py
comprehension_examples.py
generator_expression_examples.py
iteration_helper_functions.py
packing_and_unpacking.py
lambda_examples.py
各ファイルの役割は次の通り。
main.py- Unit 06 の実行入口
- 各テーマ別ファイルの関数を順番に呼び出す
comprehension_examples.py- リスト内包表記、辞書内包表記、集合内包表記を扱う
generator_expression_examples.py- ジェネレータ式とリスト内包表記の違いを扱う
iteration_helper_functions.pyenumerate、zip、sorted、reversed、any、allを扱う
packing_and_unpacking.py- パック、アンパック、複数代入、引数展開を扱う
lambda_examples.py- ラムダ式の基本的な利用場面を扱う
リポジトリ直下で仮想環境を有効化してから実行する。
PowerShell の場合:
.venv\Scripts\Activate.ps1
python src/06_pythonic_constructs/main.pyGit Bash の場合:
source .venv/Scripts/activate
python src/06_pythonic_constructs/main.pyRuff の確認は次のコマンドで行う。
python -m ruff check .
python -m ruff format --check .必要に応じてフォーマットを実行する。
python -m ruff format .次の順番で読むと、内容を追いやすい。
main.pycomprehension_examples.pygenerator_expression_examples.pyiteration_helper_functions.pypacking_and_unpacking.pylambda_examples.py
最初に main.py を読むことで、この単位全体の実行順序を把握できる。
その後、内包表記、ジェネレータ式、便利な組み込み関数、アンパック、ラムダ式の順番で読む。
Unit 06 全体の処理の流れは次の通り。
main.pyが実行されるmain()が呼び出される- 表示用の見出しを出す
- 内包表記のサンプルを実行する
- ジェネレータ式のサンプルを実行する
enumerate、zip、sorted、reversed、any、allのサンプルを実行する- パック、アンパック、複数代入のサンプルを実行する
- ラムダ式のサンプルを実行する
- 各ファイル内の
assertにより、軽い期待値確認を行う
この単位では、Pythonでよく使われる簡潔な書き方を主題にしている。
各ファイルの run_...() 関数は、テーマ別サンプルをまとめて実行するための入口として使う。
comprehension_examples.py では、リスト内包表記を使って値を変換している。
squares = [number * number for number in numbers]
even_numbers = [number for number in numbers if number % 2 == 0]squares は、numbers の各値を2乗した list を作る。
even_numbers は、条件を満たす値だけを集める。
単純な変換や絞り込みでは、内包表記を使うと「何から何を作るか」がまとまりやすい。
comprehension_examples.py では、dict と set も内包表記で作成している。
name_lengths = {name: len(name) for name in names}
normalized_categories = {category.lower() for category in categories}name_lengths は、名前をキー、文字数を値にした dict である。
normalized_categories は、小文字化したカテゴリ名を集めた set である。
list だけではなく、dict や set も内包表記で作れる点に注目する。
generator_expression_examples.py では、ジェネレータ式を作成している。
squares_generator = (number * number for number in numbers)
first_square = next(squares_generator)
second_square = next(squares_generator)ジェネレータ式は、list を先に作るのではなく、値を必要なタイミングで取り出す。
next() を呼ぶたびに、次の値が計算される。
一度取り出した値は戻らないため、同じジェネレータを何度も再利用する前提では扱わない。
iteration_helper_functions.py では、enumerate と zip を使っている。
for index, name in enumerate(names, start=1):
indexed_names.append(f"{index}: {name}")
for name, score in zip(names, scores, strict=True):
score_rows.append(f"{name}={score}")enumerate は、インデックスと要素を同時に取り出す。
zip は、複数のコレクションから対応する値を同時に取り出す。
どちらも、range(len(...)) や手動のインデックス管理を減らせる。
iteration_helper_functions.py では、any と all を使っている。
has_high_score = any(score >= 90 for score in scores)
all_passed = all(score >= 60 for score in scores)any は、1つでも条件を満たす値があれば True を返す。
all は、すべての値が条件を満たす場合に True を返す。
条件式とジェネレータ式を組み合わせると、存在確認や全体判定を簡潔に書ける。
packing_and_unpacking.py では、tuple のアンパックを扱っている。
packed_profile = "Mio", 21, "Java"
name, age, language = packed_profilepacked_profile は、複数の値をまとめた tuple として扱える。
name, age, language = packed_profile は、その tuple を分解して変数に受け取る。
複数戻り値や for の中でもよく使う考え方である。
lambda_examples.py では、sorted の key にラムダ式を渡している。
sorted_by_score = sorted(users, key=lambda user: user["score"])lambda user: user["score"] は、ユーザー情報から score を取り出す短い関数である。
このように、その場だけで使う短い関数を渡したい場合にラムダ式を使える。
複雑な処理や再利用したい処理は、def で名前を付けた方が読みやすい。
comprehension_examples.py では、内包表記と通常の for の両方を扱っている。
doubled_numbers = []
for number in numbers:
doubled_numbers.append(number * 2)
doubled_numbers_by_comprehension = [number * 2 for number in numbers]単純な変換では、内包表記の方がすっきり書ける。
ただし、処理が複雑になりすぎると、通常の for の方が読みやすい場合もある。
短く書くことより、処理の意図が読み取りやすいことを優先する。
generator_expression_examples.py では、同じジェネレータを2回 list にしている。
upper_words_generator = (word.upper() for word in words)
upper_words = list(upper_words_generator)
empty_after_consumed = list(upper_words_generator)1回目の list(upper_words_generator) で、ジェネレータの値はすべて取り出される。
そのため、2回目の list(upper_words_generator) は空の list になる。
もう一度使いたい場合は、ジェネレータ式を作り直す必要がある。
iteration_helper_functions.py では、同じ長さの names と scores を使っている。
for name, score in zip(names, scores, strict=True):
score_rows.append(f"{name}={score}")通常の zip は、複数のコレクションを同時に進める。
長さが違う場合は、短い方が終わった時点で止まる。
今回のコードでは strict=True を付けて、長さの違いをエラーとして検出できる形にしている。
iteration_helper_functions.py では、reversed の結果を list にしている。
reversed_names = list(reversed(names))reversed(names) は、逆順に取り出すためのイテレータを返す。
元の names 自体を並び替えるわけではない。
元の list を変更したい場合は、別の方法が必要になる。
packing_and_unpacking.py では、* と ** による引数展開を扱っている。
formatted_user = format_user(*user_values)
formatted_user_by_dict = format_user(**user_options)*user_values は、tuple の値を位置引数として展開する。
**user_options は、dict の値をキーワード引数として展開する。
関数定義側の *args / **kwargs と、呼び出し側の * / ** は関連しているが、読む位置によって役割が変わる。
lambda_examples.py では、短い並び替え基準としてラムダ式を使っている。
sorted_by_score = sorted(users, key=lambda user: user["score"])このように、短い処理をその場で渡す場合はラムダ式が読みやすい。
一方で、条件分岐や複数行の処理が必要な場合は、def で関数として定義した方がよい。
ラムダ式は、短く書ける場面だけで使うと読みやすさを保ちやすい。
この単位を読んだ後、次の内容を確認する。
- リスト内包表記で変換と絞り込みを書ける
- 辞書内包表記でキーと値を組み立てられる
- 集合内包表記で重複しない値の集まりを作れる
- 内包表記と通常の
forの使い分けを説明できる - ジェネレータ式が必要なタイミングで値を取り出すことを説明できる
- ジェネレータが一度消費されると空になることを説明できる
enumerateとzipの基本的な使い方を説明できるsorted、reversed、any、allの用途を説明できる- パック / アンパックと複数代入を読める
- ラムダ式の基本的な利用場面を説明できる