39. 必須の標準ライブラリモジュール
Python の 標準ライブラリ(standard library) は、Python に組み込みで付属しているモジュールの集合です。追加で何かをインストールしなくても使えます。これらのモジュールは、乱数の生成、日付と時刻の扱い、他のプログラムとのデータ交換、基本的なリストや辞書を超える専用のデータ構造の利用といった、よくあるプログラミング作業のための強力なツールを提供します。
この章では、実務の Python プログラミングで頻繁に使う 5 つの必須標準ライブラリモジュールを見ていきます。
39.1) random で乱数を生成する
random モジュールは、乱数を生成したり、ランダムに選択したりするための関数を提供します。これはシミュレーション、ゲーム、テスト、サンプリングデータ、そして予測不能な挙動が必要なあらゆる場面で役立ちます。
39.1.1) randint() でランダムな整数を生成する
randint() 関数は、2 つの値の間のランダムな整数を生成します。両端を 含む(inclusive) のが特徴です。
import random
# 6 面サイコロを振るシミュレーション
die_roll = random.randint(1, 6)
print(f"You rolled: {die_roll}") # Output: You rolled: 4 (varies each run)
# 18〜65 の間でランダムな年齢を生成
age = random.randint(18, 65)
print(f"Random age: {age}") # Output: Random age: 42 (varies)開始値と終了値の両方が、取り得る結果に 含まれている ことに注意してください。randint(1, 6) は 1, 2, 3, 4, 5, 6 を返す可能性があり、6 つすべての値があり得ます。
複数回のサイコロを振る動作をシミュレーションする実用的な例を示します。
import random
# 2 つのサイコロを振って合計を計算するシミュレーション
die1 = random.randint(1, 6)
die2 = random.randint(1, 6)
total = die1 + die2
print(f"Die 1: {die1}") # Output: Die 1: 3 (varies)
print(f"Die 2: {die2}") # Output: Die 2: 5 (varies)
print(f"Total: {total}") # Output: Total: 8 (varies)
if total == 7:
print("Lucky seven!")
elif total == 2 or total == 12:
print("Snake eyes or boxcars!")両端が含まれる理由: これにより、randint() はよくある用途に対して直感的になります。1〜6 の数値(サイコロのように)が欲しいとき、randint(1, 6) と書けば 1 も 6 も結果としてあり得ます。
39.1.2) ランダムな浮動小数点数を生成する
ランダムな小数が必要な場合は、random() (0.0〜1.0 の float を返す) または uniform() (指定した 2 つの値の間の float を返す) を使います。
import random
# 0.0〜1.0 の間でランダムな float を生成(0.0 を含み、1.0 は含まない)
probability = random.random()
print(f"Random probability: {probability:.4f}") # Output: Random probability: 0.7284 (varies)
# 15.0〜30.0 度の間でランダムな温度を生成
temperature = random.uniform(15.0, 30.0)
print(f"Temperature: {temperature:.2f}°C") # Output: Temperature: 23.47°C (varies)
# $10.00〜$99.99 の間でランダムな価格を生成
price = random.uniform(10.0, 99.99)
print(f"Price: ${price:.2f}") # Output: Price: $45.67 (varies)random() 関数は、確率値やパーセンテージが必要なときに便利です。uniform() 関数は、特定の範囲でランダムな小数が必要なときに適しています。
39.1.3) choice() でランダムに選ぶ
choice() 関数は、シーケンス(リスト、タプル、文字列)から要素を 1 つランダムに選びます。
import random
# 色をランダムに選択
colors = ["red", "blue", "green", "yellow", "purple"]
selected_color = random.choice(colors)
print(f"Selected color: {selected_color}") # Output: Selected color: green (varies)
# 参加者から勝者をランダムに選択
participants = ["Alice", "Bob", "Charlie", "Diana"]
winner = random.choice(participants)
print(f"The winner is: {winner}") # Output: The winner is: Bob (varies)
# 文字列から文字をランダムに選択
vowels = "aeiou"
random_vowel = random.choice(vowels)
print(f"Random vowel: {random_vowel}") # Output: Random vowel: i (varies)これは特に、ゲーム、ランダムサンプリング、またはランダムなテストデータの選択に役立ちます。シーケンス内の各要素は等しい確率で選ばれます。
もう少し複雑な例として、簡単なクイズゲームをシミュレーションする例を示します。
import random
# 回答付きのクイズ問題
questions = [
("What is 2 + 2?", "4"),
("What is the capital of France?", "Paris"),
("What color is the sky?", "blue")
]
# 問題をランダムに選択
question, correct_answer = random.choice(questions)
print(f"Question: {question}")
user_answer = input("Your answer: ")
if user_answer.lower() == correct_answer.lower():
print("Correct!")
else:
print(f"Wrong! The answer was: {correct_answer}")39.1.4) sample() で複数のランダムな項目を選ぶ
シーケンスから重複なしで複数の項目を選びたいときは sample() を使います。これは、山札から置き換えなしでカードを引くのと同じです。いったん選ばれた項目は、再び選ばれません。
import random
# グループ課題のために 3 人の生徒をランダムに選ぶ
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
group = random.sample(students, 3)
print(f"Group members: {group}") # Output: Group members: ['Diana', 'Alice', 'Frank'] (varies)
# 1〜50 から宝くじ番号を 5 つ引く(重複なし)
lottery_numbers = random.sample(range(1, 51), 5)
lottery_numbers.sort() # 表示のためにソート
print(f"Lottery numbers: {lottery_numbers}") # Output: Lottery numbers: [7, 15, 23, 38, 49] (varies)sample() の第 2 引数は、選択する項目数を指定します。この数はシーケンスの長さ以下でなければなりません。存在する数より多くの項目は選べません。
39.1.5) shuffle() でシーケンスをシャッフルする
shuffle() 関数は、リストの要素を その場(in place) でランダムに並べ替えます(元のリストを変更します)。
import random
# トランプの山札をシャッフル
cards = ["A♠", "K♠", "Q♠", "J♠", "10♠", "9♠", "8♠", "7♠"]
print(f"Original: {cards}")
random.shuffle(cards)
print(f"Shuffled: {cards}") # Output: Shuffled: ['Q♠', '7♠', 'A♠', '10♠', '9♠', 'J♠', 'K♠', '8♠'] (varies)
# クイズ問題をランダムな順序にするためにシャッフル
questions = ["Question 1", "Question 2", "Question 3", "Question 4"]
random.shuffle(questions)
print(f"Randomized order: {questions}") # Output: Randomized order: ['Question 3', 'Question 1', 'Question 4', 'Question 2'] (varies)39.2) 日付と時刻を扱う
datetime モジュールは、日付、時刻、そして時間間隔を扱うためのクラスを提供します。これはスケジューリング、ログ記録、継続時間の計算、そして「いつ起きたか」を追跡する必要のあるあらゆるアプリケーションにとって不可欠です。
39.2.1) 現在の日時を取得する
datetime クラスは、日付と時刻の両方の要素を持つ、特定の時点を表します。
from datetime import datetime
# 現在の日時を取得
now = datetime.now()
print(f"Current datetime: {now}")
# Output: Current datetime: 2026-01-02 14:30:45.123456
# 個別の要素にアクセス
print(f"Year: {now.year}") # Output: Year: 2026
print(f"Month: {now.month}") # Output: Month: 1
print(f"Day: {now.day}") # Output: Day: 2
print(f"Hour: {now.hour}") # Output: Hour: 14
print(f"Minute: {now.minute}") # Output: Minute: 30
print(f"Second: {now.second}") # Output: Second: 45日付だけ(時刻なし)が欲しい場合は、date クラスを使います。
from datetime import date
# 今日の日付を取得
today = date.today()
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Year: {today.year}") # Output: Year: 2026
print(f"Month: {today.month}") # Output: Month: 1
print(f"Day: {today.day}") # Output: Day: 239.2.2) 特定の日付と時刻を作成する
特定の時点を表す datetime や date オブジェクトを作成できます。
from datetime import datetime, date
# 特定の日付を作成
birthday = date(1995, 7, 15)
print(f"Birthday: {birthday}") # Output: Birthday: 1995-07-15
# 特定の日時を作成
meeting = datetime(2026, 3, 15, 14, 30) # 2026年3月15日 14:30
print(f"Meeting: {meeting}") # Output: Meeting: 2026-03-15 14:30:00これは、締め切り、予定、歴史的な日付、または固定された時点を表すのに便利です。
from datetime import date
# プロジェクトにおける重要な日付
project_start = date(2026, 1, 15)
project_end = date(2026, 6, 30)
print(f"Project duration: {project_start} to {project_end}")
# Output: Project duration: 2026-01-15 to 2026-06-3039.2.3) timedelta で時間差を計算する
timedelta クラスは期間を表します。つまり、2 つの日付または時刻の差です。これを使って、どれだけ時間が経過したかを計算したり、日付に時間を加算/減算したりできます。
from datetime import date, timedelta
# 年齢を計算
birth_date = date(1995, 7, 15)
today = date(2026, 1, 2)
age_delta = today - birth_date
print(f"Days since birth: {age_delta.days}") # Output: Days since birth: 11128
print(f"Years (approximate): {age_delta.days // 365}") # Output: Years (approximate): 30日付から別の日付を引くと、timedelta オブジェクトが得られます。days 属性はその期間に含まれる日数を教えてくれます。
特定の期間を表すために、timedelta オブジェクトを直接作ることもできます。
from datetime import date, timedelta
# 日付に日数を加える
today = date(2026, 1, 2)
one_week = timedelta(days=7)
next_week = today + one_week
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Next week: {next_week}") # Output: Next week: 2026-01-09
# 日付から日数を引く
thirty_days_ago = today - timedelta(days=30)
print(f"30 days ago: {thirty_days_ago}") # Output: 30 days ago: 2025-12-03timedelta は、日、秒、マイクロ秒、ミリ秒、分、時間、週を表せます。
from datetime import datetime, timedelta
# 締め切りを計算
now = datetime(2026, 1, 2, 14, 30)
deadline = now + timedelta(hours=48, minutes=30)
print(f"Current time: {now}") # Output: Current time: 2026-01-02 14:30:00
print(f"Deadline: {deadline}") # Output: Deadline: 2026-01-04 15:00:00
# 残り時間を計算
time_left = deadline - now
print(f"Hours remaining: {time_left.total_seconds() / 3600}") # Output: Hours remaining: 48.5total_seconds() メソッドは、期間全体を秒に変換します。そこから時間、分、または他の単位に変換できます。
プロジェクトのマイルストーンを計算する実用例を示します。
from datetime import date, timedelta
# プロジェクト計画
project_start = date(2026, 1, 15)
sprint_duration = timedelta(weeks=2)
sprint_1_end = project_start + sprint_duration
sprint_2_end = sprint_1_end + sprint_duration
sprint_3_end = sprint_2_end + sprint_duration
print(f"Sprint 1: {project_start} to {sprint_1_end}")
# Output: Sprint 1: 2026-01-15 to 2026-01-29
print(f"Sprint 2: {sprint_1_end} to {sprint_2_end}")
# Output: Sprint 2: 2026-01-29 to 2026-02-12
print(f"Sprint 3: {sprint_2_end} to {sprint_3_end}")
# Output: Sprint 3: 2026-02-12 to 2026-02-2639.2.4) 日付と時刻を比較する
date と datetime オブジェクトは、標準の比較演算子で比較できます。
from datetime import date
# 日付を比較
date1 = date(2026, 1, 15)
date2 = date(2026, 2, 20)
date3 = date(2026, 1, 15)
print(date1 < date2) # Output: True
print(date1 == date3) # Output: True
print(date2 > date1) # Output: Trueこれは、締め切りのチェック、日付範囲の検証、日付のソートに役立ちます。
from datetime import date
# 日付が過去かどうかをチェック
event_date = date(2025, 12, 25)
today = date(2026, 1, 2)
if event_date < today:
print("This event has already passed") # Output: This event has already passed
else:
print("This event is upcoming")
# 日付のリストをソート
important_dates = [
date(2026, 3, 15),
date(2026, 1, 10),
date(2026, 2, 28)
]
important_dates.sort()
print("Dates in order:") # Output: Dates in order:
for d in important_dates:
print(f" {d}")
# Output:
# 2026-01-10
# 2026-02-28
# 2026-03-1539.2.5) strftime() で日付と時刻をフォーマットする
strftime() メソッド(string format time)は、日付と時刻をフォーマットされた文字列に変換します。フォーマットは特殊なコードで指定します。
from datetime import datetime
now = datetime(2026, 1, 2, 14, 30, 45)
# よく使う日付フォーマット
print(now.strftime("%Y-%m-%d")) # Output: 2026-01-02
print(now.strftime("%m/%d/%Y")) # Output: 01/02/2026
print(now.strftime("%B %d, %Y")) # Output: January 02, 2026
print(now.strftime("%A, %B %d, %Y")) # Output: Friday, January 02, 2026
# よく使う時刻フォーマット
print(now.strftime("%H:%M:%S")) # Output: 14:30:45
print(now.strftime("%I:%M %p")) # Output: 02:30 PM
# 組み合わせフォーマット
print(now.strftime("%Y-%m-%d %H:%M:%S")) # Output: 2026-01-02 14:30:45
print(now.strftime("%B %d, %Y at %I:%M %p")) # Output: January 02, 2026 at 02:30 PMよく使うフォーマットコード:
| Code | Description | Example |
|---|---|---|
%Y | 世紀付きの年 | 2026 |
%m | 0 埋めされた月(01-12) | 01 |
%d | 0 埋めされた日(01-31) | 02 |
%B | 月名(フル) | January |
%b | 月名(短縮) | Jan |
%A | 曜日名(フル) | Friday |
%a | 曜日名(短縮) | Fri |
%H | 24 時間表記の時(00-23) | 14 |
%I | 12 時間表記の時(01-12) | 02 |
%M | 分(00-59) | 30 |
%S | 秒(00-59) | 45 |
%p | AM/PM | PM |
ログエントリを作る実用例を示します。
from datetime import datetime
def log_event(message):
"""タイムスタンプ付きでイベントをログに記録する"""
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {message}")
log_event("User logged in")
# Output: [2026-01-02 14:30:45] User logged in
log_event("File uploaded successfully")
# Output: [2026-01-02 14:30:45] File uploaded successfully39.2.6) strptime() で文字列から日付を解析する
strptime() 関数(string parse time)は、フォーマットされた文字列を datetime オブジェクトへ戻します。同じフォーマットコードを指定して、文字列をどのように解釈するかを Python に伝えます。
from datetime import datetime
# 異なる日付フォーマットを解析
date_str1 = "2026-01-15"
date1 = datetime.strptime(date_str1, "%Y-%m-%d")
print(f"Parsed: {date1}") # Output: Parsed: 2026-01-15 00:00:00
date_str2 = "January 15, 2026"
date2 = datetime.strptime(date_str2, "%B %d, %Y")
print(f"Parsed: {date2}") # Output: Parsed: 2026-01-15 00:00:00
# 時刻付きの日時を解析
datetime_str = "2026-01-15 14:30:00"
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
print(f"Parsed: {dt}") # Output: Parsed: 2026-01-15 14:30:00これは、ファイル、ユーザー入力、外部データソースから日付を読み取るときに不可欠です。
from datetime import datetime
# ユーザー入力を解析
user_input = "03/15/2026"
try:
event_date = datetime.strptime(user_input, "%m/%d/%Y")
print(f"Event scheduled for: {event_date.strftime('%B %d, %Y')}")
# Output: Event scheduled for: March 15, 2026
except ValueError:
print("Invalid date format. Please use MM/DD/YYYY")重要: フォーマット文字列は入力文字列と完全に一致していないといけません。一致しない場合は ValueError になります。
from datetime import datetime
# これは失敗します - フォーマットが一致しない
try:
datetime.strptime("2026-01-15", "%m/%d/%Y") # Wrong format
except ValueError as e:
print(f"Error: {e}")
# Output: Error: time data '2026-01-15' does not match format '%m/%d/%Y'39.3) JSON データを読み書きする
JSON (JavaScript Object Notation) は、構造化データを保存・交換するためのテキスト形式です。Web API、設定ファイル、プログラム間のデータ交換で最も一般的な形式です。Python の json モジュールを使うと、Python のデータ構造と JSON テキストの間を簡単に変換できます。
39.3.1) JSON 構造を理解する
JSON は Python の辞書やリストに似ていますが、いくつか違いがあります。
JSON がサポートするデータ型:
- オブジェクト(Python の辞書のようなもの):
{"name": "Alice", "age": 30} - 配列(Python のリストのようなもの):
[1, 2, 3, 4] - 文字列:
"hello"(ダブルクォート必須) - 数値:
42,3.14 - 真偽値:
true,false(小文字) - null:
null(Python のNoneに相当)
Python との主な違い:
- JSON は Python の
True/False/Noneの代わりにtrue/false/nullを使う - JSON の文字列はシングルクォートではなくダブルクォート(
"text")を使う必要がある - JSON はタプル、集合、カスタムオブジェクトを直接サポートしない
JSON データは次のようになります。
{
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": true,
"scores": [85, 92, 78, 95],
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "12345"
}
}Note: これは純粋な JSON テキストで、Python コードではありません。小文字の true とダブルクォートの使用に注目してください。
39.3.2) dumps() で Python データを JSON に変換する
dumps() 関数(dump string)は、Python のデータ構造を JSON 形式の文字列に変換します。
import json
student = {
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": True,
"scores": [85, 92, 78, 95]
}
# 辞書を JSON に変換
json_string = json.dumps(student)
print(json_string)
# Output: {"name": "Alice Johnson", "age": 30, "email": "alice@example.com", "is_active": true, "scores": [85, 92, 78, 95]}
print(type(json_string)) # Output: <class 'str'>出力では Python の True が JSON の true になっていることに注目してください。dumps() 関数はこれらの変換を自動的に処理します。
読みやすい出力にするには、indent パラメータを使います。
import json
student = {
"name": "Alice Johnson",
"age": 30,
"scores": [85, 92, 78, 95]
}
# インデント付きで整形して出力
json_string = json.dumps(student, indent=2)
print(json_string)
# Output:
# {
# "name": "Alice Johnson",
# "age": 30,
# "scores": [
# 85,
# 92,
# 78,
# 95
# ]
# }indent パラメータは、各インデントレベルで使うスペース数を指定します。これにより、特に複雑な入れ子構造の場合に JSON が読みやすくなります。
39.3.3) loads() で JSON を Python データに変換する
loads() 関数(load string)は、JSON 形式の文字列を Python のデータ構造に戻します。
import json
# JSON 文字列(Web API から受け取るようなもの)
json_string = '{"name": "Bob Smith", "age": 25, "scores": [90, 88, 92]}'
# Python の辞書へ変換
student = json.loads(json_string)
print(student) # Output: {'name': 'Bob Smith', 'age': 25, 'scores': [90, 88, 92]}
print(type(student)) # Output: <class 'dict'>
# ふつうの Python 辞書と同様にデータへアクセス
print(f"Name: {student['name']}") # Output: Name: Bob Smith
print(f"Average score: {sum(student['scores']) / len(student['scores'])}")
# Output: Average score: 90.0JSON の true、false、null は、Python の True、False、None に自動的に変換されます。
import json
json_string = '{"active": true, "verified": false, "middle_name": null}'
data = json.loads(json_string)
print(data) # Output: {'active': True, 'verified': False, 'middle_name': None}
print(type(data["active"])) # Output: <class 'bool'>
print(type(data["middle_name"])) # Output: <class 'NoneType'>39.3.4) dump() で JSON をファイルへ書き込む
dump() 関数は、Python データを JSON 形式でファイルへ直接書き込みます。
import json
# 学生の記録
students = [
{"name": "Alice", "age": 20, "gpa": 3.8},
{"name": "Bob", "age": 22, "gpa": 3.5},
{"name": "Charlie", "age": 21, "gpa": 3.9}
]
# JSON ファイルに書き込む
with open("students.json", "w") as file:
json.dump(students, file, indent=2)
print("Data written to students.json")
# Output: Data written to students.jsonこのコードを実行すると、ファイル students.json の中身は次のようになります。
[
{
"name": "Alice",
"age": 20,
"gpa": 3.8
},
{
"name": "Bob",
"age": 22,
"gpa": 3.5
},
{
"name": "Charlie",
"age": 21,
"gpa": 3.9
}
]なぜ dumps() ではなく dump() を使うのですか? dump() 関数はファイルへ直接書き込むため、いったん文字列に変換してからその文字列を書き込むより効率的です。ファイルには dump() を使い、JSON を文字列として必要なとき(たとえばネットワークで送る場合)は dumps() を使います。
39.3.5) load() で JSON をファイルから読み込む
load() 関数は、ファイルから JSON データを読み込み、Python のデータ構造へ変換します。
import json
# 先ほど作成した JSON ファイルから読み込む
with open("students.json", "r") as file:
students = json.load(file)
print(f"Loaded {len(students)} students") # Output: Loaded 3 students
# データを扱う
for student in students:
print(f"{student['name']}: GPA {student['gpa']}")
# Output:
# Alice: GPA 3.8
# Bob: GPA 3.5
# Charlie: GPA 3.939.3.6) JSON のエラーを扱う
JSON を扱うと、不正なデータに遭遇することがあります。潜在的なエラーは必ず処理するようにしましょう。
import json
# 不正な JSON - 閉じクォートがない
invalid_json = '{"name": "Alice", "age": 30'
try:
data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
# Output: Invalid JSON: Expecting ',' delimiter: line 1 column 28 (char 27)これは特に、JSON を外部ソース(ファイル、Web API、ユーザー入力)から読み込む場合に重要です。データが常に正しいとは限りません。
import json
def load_config(filename):
"""エラー処理付きで JSON ファイルから設定を読み込む"""
try:
with open(filename, "r") as file:
config = json.load(file)
return config
except FileNotFoundError:
print(f"Config file '{filename}' not found")
return None
except json.JSONDecodeError as e:
print(f"Invalid JSON in '{filename}': {e}")
return None
# 設定の読み込みを試す
config = load_config("config.json")
if config:
print(f"Configuration loaded: {config}")
else:
print("Using default configuration")39.3.7) 実用的な JSON の例: アプリケーション状態の保存と読み込み
アプリケーションデータを保存して読み込む方法を示す完全な例です。
import json
def save_game_state(filename, player_data):
"""ゲーム状態を JSON ファイルに保存する"""
with open(filename, "w") as file:
json.dump(player_data, file, indent=2)
print(f"Game saved to {filename}")
def load_game_state(filename):
"""ゲーム状態を JSON ファイルから読み込む"""
try:
with open(filename, "r") as file:
player_data = json.load(file)
print(f"Game loaded from {filename}")
return player_data
except FileNotFoundError:
print("No saved game found")
return None
# ゲームデータ
player = {
"name": "Hero",
"level": 5,
"health": 85,
"inventory": ["sword", "shield", "potion"],
"position": {"x": 10, "y": 20}
}
# ゲームを保存
save_game_state("savegame.json", player)
# Output: Game saved to savegame.json
# 後でゲームを読み込む
loaded_player = load_game_state("savegame.json")
# Output: Game loaded from savegame.json
if loaded_player:
print(f"Welcome back, {loaded_player['name']}!")
print(f"Level: {loaded_player['level']}, Health: {loaded_player['health']}")
# Output:
# Welcome back, Hero!
# Level: 5, Health: 8539.4) collections の実用的なコンテナ
collections モジュールは、Python の組み込みコンテナ(リスト、辞書、集合)を追加機能で拡張する専用のコンテナ型を提供します。これらのコンテナは、基本的なデータ構造を使うよりも一般的な問題をよりエレガントに解決します。
39.4.1) Counter で項目を数える
Counter クラスは、ハッシュ可能なオブジェクトのカウント用に設計されています。これは辞書のサブクラスで、項目をキー、そのカウントを値として保持します。
Counter が入力として受け取れるもの:
- 任意の反復可能(iterable)オブジェクト(リスト、文字列、タプルなど)
- カウント付きの別の辞書
- カウントを指定するキーワード引数
Counter が保存するもの:
- キーが項目で、値がカウントである辞書
- 例:
Counter(['a', 'b', 'a'])は{'a': 2, 'b': 1}を保持します
通常の辞書に対する主な利点:
- 存在しないキーでも
KeyErrorを投げずに 0 を返す most_common()のようなカウント専用メソッドを提供する- Counter 同士の算術演算をサポートする
基本的な使い方
from collections import Counter
# 単語内の文字を数える
word = "mississippi"
letter_counts = Counter(word)
print(letter_counts)
# Output: Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
# 辞書のようにカウントへアクセス
print(f"Number of 'i's: {letter_counts['i']}")
# Output: Number of 'i's: 4
print(f"Number of 'z's: {letter_counts['z']}")
# Output: Number of 'z's: 0 (returns 0 for missing keys, no KeyError!)異なる入力元から Counter を作成する
from collections import Counter
# リストから
votes = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "Alice"]
vote_counts = Counter(votes)
print(vote_counts)
# Output: Counter({'Alice': 4, 'Bob': 2, 'Charlie': 1})
# 文字列から(各文字を数える)
letter_counts = Counter("hello")
print(letter_counts)
# Output: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
# 辞書から
existing_counts = {'apple': 3, 'banana': 2}
fruit_counts = Counter(existing_counts)
print(fruit_counts)
# Output: Counter({'apple': 3, 'banana': 2})
# キーワード引数から
color_counts = Counter(red=5, blue=3, green=2)
print(color_counts)
# Output: Counter({'red': 5, 'blue': 3, 'green': 2})most_common() で最頻出項目を見つける
メソッドシグネチャ: most_common(n=None)
パラメータ:
n(任意): 返す上位項目数nを省略するかNoneの場合は、全項目を返します
戻り値:
(item, count)タプルのリスト- カウントの降順でソートされます
- カウントが同じ場合は、最初に出現した順になります
from collections import Counter
# テキスト内の単語頻度を分析
text = "the quick brown fox jumps over the lazy dog the fox"
words = text.split()
word_counts = Counter(words)
# 上位 3 語を取得
top_3 = word_counts.most_common(3)
print(top_3)
# Output: [('the', 3), ('fox', 2), ('quick', 1)]Counter の算術演算
Counter オブジェクト同士で、加算、減算、その他の演算ができます。
from collections import Counter
# 2 つのグループ内の項目をカウント
group1 = Counter(["apple", "banana", "apple", "orange"])
print(group1)
# Output: Counter({'apple': 2, 'banana': 1, 'orange': 1})
group2 = Counter(["banana", "banana", "grape", "apple"])
print(group2)
# Output: Counter({'banana': 2, 'grape': 1, 'apple': 1})
# カウントを合算
combined = group1 + group2
print(combined)
# Output: Counter({'apple': 3, 'banana': 3, 'orange': 1, 'grape': 1})
# カウントを減算(正の結果のみを保持)
difference = group1 - group2
print(difference)
# Output: Counter({'apple': 1, 'orange': 1})
# banana: 1 - 2 = -1 (negative, so excluded)
# grape: not in group1, so excluded実用例: 生徒の成績を分析する
from collections import Counter
# 成績の分布
grades = ["A", "B", "A", "C", "B", "A", "B", "D", "A", "B", "C", "A"]
grade_counts = Counter(grades)
print(f"Total students: {len(grades)}")
# Output: Total students: 12
print("\nGrade Distribution:")
for grade, count in grade_counts.most_common():
percentage = (count / len(grades)) * 100
bar = "█" * count
print(f" {grade}: {count} students ({percentage:4.1f}%) {bar}")
# Output:
# Grade Distribution:
# A: 5 students (41.7%) █████
# B: 4 students (33.3%) ████
# C: 2 students (16.7%) ██
# D: 1 students ( 8.3%) █39.4.2) defaultdict でデフォルト値を持つ辞書を使う
defaultdict クラスは辞書のサブクラスで、存在しないキーへアクセスしたときにデフォルト値のエントリを自動的に作成します。これにより、キーが存在するかどうかを使う前にチェックする必要がなくなります。
defaultdict が入力として受け取るもの:
- デフォルトファクトリ(default factory) 関数(必須): 存在しないキーに対するデフォルト値を返す callable
- 通常の
dictが受け取れる任意の引数(キーと値のペア、別の辞書、キーワード引数)
通常の辞書に対する主な利点:
- 使う前にキーの存在チェックが不要
- 存在しないキーをデフォルト値で自動初期化
- グルーピング、カウント、累積処理のコードがより読みやすくなる
デフォルトファクトリを理解する
defaultdict を作成するには デフォルトファクトリ を指定する必要があります。これは引数なしで呼び出せて、存在しないキーのデフォルト値を返す callable(関数)です。よく使うデフォルトファクトリは次のとおりです。
int-0を返す(カウントに便利)list-[]を返す(項目のグルーピングに便利)set-set()を返す(ユニークな項目の収集に便利)str-''を返す(文字列連結に便利)lambda: value- 任意のデフォルト値を返す
from collections import defaultdict
# 異なるデフォルトファクトリ
counts = defaultdict(int) # 存在しないキーは 0 を返す
groups = defaultdict(list) # 存在しないキーは [] を返す
unique = defaultdict(set) # 存在しないキーは set() を返す
custom = defaultdict(lambda: "N/A") # 存在しないキーは "N/A" を返す
# 存在しないキーでテスト
print(counts['missing']) # Output: 0
print(groups['missing']) # Output: []
print(unique['missing']) # Output: set()
print(custom['missing']) # Output: N/A基本的な使い方: defaultdict でカウントする
カウント処理における通常の辞書と defaultdict を比較します。
from collections import defaultdict
word = "mississippi"
# 通常の辞書 - キーが存在するかチェックが必要
regular_dict = {}
for letter in word:
if letter not in regular_dict:
regular_dict[letter] = 0
regular_dict[letter] += 1
print(regular_dict)
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}
# defaultdict - デフォルト値でエントリを自動作成
letter_counts = defaultdict(int) # int() は 0 を返す
for letter in word:
letter_counts[letter] += 1 # キー存在チェックは不要!
print(dict(letter_counts))
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}仕組み:
- 新しい文字に対して
letter_counts[letter]へアクセスすると、defaultdictはint()を呼び出して0を返します - キーが値
0で作成され、+= 1によって1になります - 既存のキーは、通常の辞書と同様に振る舞います
defaultdict(list) で項目をグルーピングする
よくある用途は、項目をカテゴリごとにグルーピングすることです。
from collections import defaultdict
students = [
("Alice", "A"),
("Bob", "B"),
("Charlie", "A"),
("Diana", "C"),
("Eve", "B"),
("Frank", "A")
]
# 成績で生徒をグルーピング
# defaultdict ならシンプルで分かりやすい
students_by_grade = defaultdict(list)
for name, grade in students:
students_by_grade[grade].append(name)
print(dict(students_by_grade))
# Output: {'A': ['Alice', 'Charlie', 'Frank'], 'B': ['Bob', 'Eve'], 'C': ['Diana']}
# まだ存在しない成績へアクセス
print(students_by_grade["D"]) # Output: [] (empty list, not KeyError!)仕組み:
- 新しい成績に対して
students_by_grade[grade]へアクセスすると、defaultdictはlist()を呼び出して[]を返します - そのキーは空のリストで作成され、
.append(name)により最初の生徒が追加されます - 既存の成績は、既存リストに追加するだけです
既存の辞書から defaultdict を作成する
既存データで defaultdict を初期化できます。
from collections import defaultdict
# 既存のカウントから開始
existing_data = {'apple': 5, 'banana': 3}
# 既存の辞書から defaultdict を作成
fruit_counts = defaultdict(int, existing_data)
# さらにカウントを追加
fruit_counts['apple'] += 2 # 5 + 2 = 7
fruit_counts['orange'] += 1 # 0 + 1 = 1 (new key, starts at 0)
print(dict(fruit_counts))
# Output: {'apple': 7, 'banana': 3, 'orange': 1}カスタムのデフォルトファクトリ
デフォルトファクトリには任意の callable を指定できます。
from collections import defaultdict
# カスタムのデフォルト値に lambda を使う
page_views = defaultdict(lambda: {'views': 0, 'unique': 0})
page_views['home']['views'] = 100
page_views['home']['unique'] = 75
print(page_views['home'])
# Output: {'views': 100, 'unique': 75}
print(page_views['about']) # New key gets default dictionary
# Output: {'views': 0, 'unique': 0}重要な注意点
キーへのアクセスとキーの存在確認:
from collections import defaultdict
counts = defaultdict(int)
# 存在しないキーへのアクセスは作成につながる
value = counts['missing'] # 'missing' を値 0 で作成する
print('missing' in counts) # Output: True
# 作成せずに確認するには 'in' または .get() を使う
counts2 = defaultdict(int)
print('missing' in counts2) # Output: False (doesn't create key)
print(counts2.get('missing')) # Output: None (doesn't create key)39.5) (Optional) 便利な反復処理ツール
itertools モジュールは、効率的なイテレータ(iterator)を作成するための関数を提供します。これらのツールにより、大きな中間リストを作らずに、強力な方法でシーケンスを扱えます。
39.5.1) chain() で iterable を連結する
chain() 関数は、複数の iterable を 1 つのイテレータへまとめ、各 iterable の要素を順番に yield します。
chain() が受け取るもの:
- 複数の iterable(リスト、タプル、文字列など)を個別引数として受け取ります
chain() が返すもの:
- 最初の iterable の全要素、次に 2 番目の iterable の全要素…という順に要素を yield するイテレータ
主な利点:
+で連結するよりメモリ効率が良い(中間リストを作らない)- リストだけでなく任意の iterable で動作する
from itertools import chain
# 複数のリストを結合
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
combined = chain(list1, list2, list3)
print(list(combined)) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]これは、特に大きなシーケンスに対して、+ でリストを連結するよりメモリ効率が良いです。
from itertools import chain
# 大きな結合リストを作らずに複数のデータソースを処理
students_class_a = ["Alice", "Bob", "Charlie"]
students_class_b = ["Diana", "Eve", "Frank"]
students_class_c = ["Grace", "Henry", "Iris"]
# 結合リストを作らずに全生徒を反復処理
for student in chain(students_class_a, students_class_b, students_class_c):
print(f"Processing: {student}")
# Output:
# Processing: Alice
# Processing: Bob
# Processing: Charlie
# Processing: Diana
# Processing: Eve
# Processing: Frank
# Processing: Grace
# Processing: Henry
# Processing: Iris異なる種類の iterable も chain できます。
from itertools import chain
# リスト、タプル、文字列を chain する
numbers = [1, 2, 3]
letters = ("a", "b", "c")
word = "xyz"
combined = chain(numbers, letters, word)
print(list(combined)) # Output: [1, 2, 3, 'a', 'b', 'c', 'x', 'y', 'z']39.5.2) cycle() で要素を繰り返す
cycle() 関数は、iterable の要素を繰り返し巡回する無限イテレータを作成します。
cycle() が受け取るもの:
- 1 つの iterable(リスト、タプル、文字列など)
cycle() が返すもの:
- iterable の要素を繰り返し yield する無限のイテレータ
- 終端に到達したら先頭から再開します
主な特徴:
- 無限(infinite) イテレータを作るため、自分で止めない限り止まりません
- 停止条件(カウンタ、
break、またはzip())と一緒に使う必要があります - メモリ効率が良い: データのコピーを作りません
from itertools import cycle
# 色の無限サイクルを作成
colors = cycle(["red", "green", "blue"])
# 最初の 10 個の色を取り出す
for i, color in enumerate(colors):
if i >= 10:
break
print(f"Item {i}: {color}")
# Output:
# Item 0: red
# Item 1: green
# Item 2: blue
# Item 3: red
# Item 4: green
# Item 5: blue
# Item 6: red
# Item 7: green
# Item 8: blue
# Item 9: redWarning: cycle() は無限イテレータを作成します。停止条件(カウンタや break 文など)と一緒に使わないと無限ループになります。
実用例として、値を交互に切り替えるケースがあります。
from itertools import cycle
# テーブル行の背景色を 2 色で交互にする
row_colors = cycle(["white", "lightgray"])
rows = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"]
for row, color in zip(rows, row_colors):
print(f"{row}: background-color: {color}")
# Output:
# Row 1: background-color: white
# Row 2: background-color: lightgray
# Row 3: background-color: white
# Row 4: background-color: lightgray
# Row 5: background-color: whiteここでは zip()(第 37 章で学びました)を使い、各行と色を組み合わせています。cycle() のイテレータは、必要に応じて色を自動的に繰り返します。
39.5.3) chain() と cycle() を組み合わせる
itertools の関数は、より複雑なパターンのために組み合わせて使えます。
from itertools import chain, cycle
# 複数シーケンスを巡回するパターンを作成
pattern1 = [1, 2, 3]
pattern2 = [10, 20]
# パターンを chain して、その結果を cycle する
combined_pattern = cycle(chain(pattern1, pattern2))
# 最初の 12 個の値を取り出す
for i, value in enumerate(combined_pattern):
if i >= 12:
break
print(value, end=" ")
# Output: 1 2 3 10 20 1 2 3 10 20 1 2
print() # Output: Newlineこれにより、1, 2, 3, 10, 20, 1, 2, 3, 10, 20, ... という繰り返しパターンが作られます。
チームメンバーのローテーションスケジュールを作る実用例を示します。
from itertools import cycle
# チームメンバーのローテーションスケジュールを作成
team_members = ["Alice", "Bob", "Charlie"]
schedule = cycle(team_members)
# タスクをローテーションで割り当てる
tasks = [
"Review code",
"Write tests",
"Update documentation",
"Fix bug #123",
"Implement feature X",
"Deploy to staging"
]
print("Task Assignments:")
for task, assignee in zip(tasks, schedule):
print(f" {assignee}: {task}")
# Output:
# Task Assignments:
# Alice: Review code
# Bob: Write tests
# Charlie: Update documentation
# Alice: Fix bug #123
# Bob: Implement feature X
# Charlie: Deploy to stagingこの章では、Python の機能を拡張する 5 つの必須標準ライブラリモジュールを見てきました。
random: 乱数を生成し、ランダムな選択を行い、シーケンスをシャッフルする—シミュレーション、ゲーム、テストに不可欠datetime: 日付、時刻、期間を扱う—年齢の計算、イベントのスケジュール、タイムスタンプのフォーマットjson: 普遍的な JSON 形式で他のプログラムとデータ交換する—アプリ状態の保存、Web API の利用、設定の保存collections: カウント用のCounterやキーを自動作成するdefaultdictのような専用コンテナを使うitertools:chain()でシーケンスを結合し、cycle()でパターンを繰り返すなど、効率的なイテレータを作成する
これらのモジュールは Python の標準ライブラリの一部です。いつでも利用でき、十分にテストされており、よくあるプログラミング問題をエレガントに解決します。より複雑なプログラムを作るようになると、これらのツールを頻繁に使うようになるでしょう。これらは「batteries included」という Python の哲学を体現しており、日常的なプログラミング作業のために強力で、すぐに使える解決策を提供します。