Python速習ガイド - ステップ1 基本的な文法とデータ型 (9) - 辞書
- 本講座は、Pythonプログラミングの基礎を手を動かしながら最速で身につけるための講座です。
- 「スタイルガイド」では、Pythonできれいなコードを書くためのガイドライン(PEP8)で紹介されている内容を主に記載しています。
- 各コードは実行して結果を確認することができます。
ページの再読み込みで元の内容に戻りますので、自由にいじってみてください。
「ステップ1 基本的な文法とデータ型」の続きです。
1.5. データをまとめる
1.5.3. 辞書
データをまとめるデータ型として、リストやタプルとは異なるアプローチの 辞書 (dict
) があります。
辞書は「キー(key)」と「値(value)」のペアを保持するデータ型で、実際の辞書の「見出し語」と「説明」の関係に似ています。
辞書の作成方法
- 辞書は波括弧(
{
,}
)内にキー: 値
のペアをカンマ(,
)区切りで並べて作成します。
- 空の辞書は
{}
で作成します。
dict
関数を使用して辞書を作成することもできます。
- コロン(
:
)の後にはスペースを1個分空けましょう。 - カンマ(
,
)の後にはスペースを1個分空けましょう。
- 長い辞書の場合は複数行に分けると読みやすくなります。
- 各要素は行の先頭に スペース4個分 の字下げ(インデント)を追加します。
- 最後の要素の後ろにもカンマを入れましょう。
以下の商品情報を持つ辞書を作成してみましょう。
- 商品名(
product
): 食パン - 価格(
price
): 250 - 賞味期限(
expiration_date
): “2025年6月8日” (文字列として入力してください) - 在庫数(
stock
): 15 - 新商品かどうか(
is_new
): False
問題なく作成できたら、dict
関数を使って同じ辞書を作成してみましょう。
辞書のキーの制約
辞書のキーには イミュータブルな (変更不可能な)オブジェクトが使用できます。
- 数値、文字列、(内部にミュータブルな要素を持たない)タプル、ブール値などはイミュータブルなため、キーとして使用可能です。
- リストや辞書はキーとして使用できません。
- ミュータブルな (変更可能な)オブジェクトでは、値が変更されてしまうとキーとしての役割を果たせなくなるため、基本的に使用できません。(詳しくはコラムを参照)
Pythonの辞書はキーを使って値を素早く検索するために「ハッシュ」と呼ばれる技術を使っています。
これはキーに対して特別な「ハッシュ値」という数値を計算して、その値をもとに検索するというものです。
このハッシュ値が計算できるオブジェクトのことを「ハッシュ化可能(hashable)」といいます。
ハッシュ化可能かどうかが辞書のキーとして使えるかどうかの基準になります。
基本的には以下のような特徴があります:
- イミュータブル(変更不可能)なオブジェクトは通常ハッシュ化可能です
- ミュータブル(変更可能)なオブジェクトは通常ハッシュ化不可能です
ミュータブルなオブジェクトがハッシュ化不可能なのは、内容が変わるとハッシュ値も変わってしまうため、辞書の検索システムが正しく機能しなくなるためです。
簡単にまとめると:
- ハッシュ化可能(辞書のキーに使える): 文字列、数値、タプル(中身がすべてイミュータブルの場合)、None、ブール値など
- ハッシュ化不可能(辞書のキーに使えない): リスト、辞書など
技術的には、ハッシュ値を適切に定義すればミュータブルなオブジェクトもキーとして使用可能ですが、
そのような使い方は一般的ではなく、データの整合性を保つのが難しくなります。
この点は上級者向けのため、まずは「イミュータブルなものがキーに使える」と覚えておけば十分です。
より詳しい内容は、後に学ぶオブジェクト指向プログラミングの中で理解できるようになります。
辞書の基本的な操作
要素の取得
- 辞書の要素はキーを指定して取得します。
- キーが存在しない場合は
KeyError
が発生します。
要素の追加・変更
- 新しいキーと値のペアを追加するには、単にキーを指定して値を代入します。
- 既存のキーを指定すると、その値が更新されます。
存在チェック (in
, not in
)
- キーが辞書に存在するかどうかを
in
演算子で確認できます。
辞書のメソッド
要素取得 (get
)
get
メソッドを使うと、キーが存在しない場合にエラーではなくデフォルト値を返すことができます。- デフォルト値を指定しない場合は
None
が返ります。
- デフォルト値を指定しない場合は
キー一覧・値一覧取得 (keys
, values
, items
)
keys
メソッドは、キー(key)の一覧を返します。- 戻り値は
dict_keys
型というリストとは異なる型ですが、in
演算子による存在チェックなどが可能です。
- 戻り値は
values
メソッドは値(value)の一覧を返します。- 戻り値は
dict_values
型というリストとは異なる型ですが、in
演算子による存在チェックなどが可能です。
- 戻り値は
items
メソッドは、キーと値のペア(タプル)の一覧を返します。- 戻り値は
dict_items
型というリストとは異なる型ですが、in
演算子による存在チェックなどが可能です。
- 戻り値は
- 返される
dict_keys
,dict_values
,dict_items
型のデータはリストと似た性質を持ちますが、
インデックス指定による要素取得など、リストで可能な様々な操作が使用できません。
上記の .keys()
, .values()
, .items()
メソッドが返す dict_keys
, dict_values
, dict_items
は
「ビューオブジェクト」と呼ばれるものです。ビューオブジェクトには以下のような特徴があります:
- 動的な参照 - 元の辞書への動的な参照であり、元の辞書が変更されるとビューも自動的に更新されます。
- メモリ効率 - 元の辞書のデータをコピーしないため、メモリ効率が良いです。
- イテレーション可能 -
for
ループなどで要素を繰り返し処理できます。(for
文については後で学習します) - 変更不可 - ビュー自体を直接変更することはできません(元の辞書を変更することで間接的に変わります)。
- リストとして使用したい場合は
list
関数でリストに変換します。リストに変換すると元の辞書との動的な連携は失われ、その時点でのスナップショットが作成されます。
辞書拡張・更新 (update
)
update
メソッドを使うと、別の辞書の内容で現在の辞書を更新できます。- 同じキーがある場合は値が上書きされ、新しいキーは追加されます。
要素を取り出す (pop
)
pop
メソッドはキーを指定して要素を取り出し、辞書から削除します。- 戻り値として、指定したキーに対応する値(value)が返ります。
- キーが存在しない場合のデフォルト値を2つ目の引数で指定できます。
- デフォルト値を指定せず、キーが存在しない場合は
KeyError
が発生します。
辞書のコピー (copy
)
リストと同様に、辞書にも浅いコピーを行うcopy
メソッドがあります。
浅いコピー(シャローコピー)であるため、リストと同様に内側の要素はコピーされないことに注意してください。
- 深いコピーを行いたい場合はリストと同様に、
copy
モジュールのdeepcopy
関数を使用します。
bool
関数による型変換
- 辞書に
bool
関数を適用すると、空の辞書({}
)の場合にFalse
、それ以外にTrue
を返します。
これまで学んだデータ型は、それぞれ異なる特徴と適した用途があります。
-
辞書 (
dict
) を使うのが適切な場合:- キーと値のペアでデータを関連付ける必要がある場合
- 名前やIDなどで素早くデータを検索したい場合
- データに意味のあるラベルをつけたい場合
-
タプル (
tuple
) を使うのが適切な場合:- 変更されない固定のデータの集まりを扱う場合
- 複数の値をグループ化したいが、後で変更する必要がない場合
- 辞書のキーとして使用する場合
-
リスト (
list
) を使うのが適切な場合:- データの順序が重要な場合
- 要素を追加、削除、変更する必要がある場合
- 同じ型の複数の要素を集めたい場合
それぞれの特性を理解し、状況に応じて適切なデータ型を選択することが重要です。
辞書のまとめ
ここまで見てきた辞書に関する操作について、練習問題を解いてみましょう。
商品と価格を管理する辞書に対して、以下の操作を行いましょう。
menu
という辞書を作成し、以下の商品と価格を登録してください- “コーヒー”: 300
- “紅茶”: 280
- “サンドイッチ”: 350
- “パスタ”: 600
- “サラダ”(500円)を追加してください
- “コーヒー"の価格を320円に更新してください
- メニューに"ケーキ"があるか確認し、結果を表示してください
- “オムライス"の価格を表示してください。メニューにない場合は"取り扱いなし"と表示してください(
get
メソッドを使用) - “パスタ"をメニューから削除し、価格を表示してください
- 商品名と価格のペアのリストを表示してください
解答例
連絡先を管理する住所録を辞書を使って実装しましょう。
contacts
という空の辞書を作成してください- 以下の連絡先情報を追加してください
"田中太郎": {"email": "[email protected]", "phone": "090-1234-5678", "city": "東京"}
"鈴木花子": {"email": "[email protected]", "phone": "080-8765-4321", "city": "大阪"}
"佐藤次郎"
を追加し、emailは"[email protected]"
、phoneは"070-2222-3333"
、cityは"名古屋"
としてください"田中太郎"
の電話番号を"090-9999-8888"
に更新してください"鈴木花子"
の連絡先をすべて表示してください- すべての連絡先の名前を表示してください(
keys
メソッドを使用) - 昔の電話番号帳が見つかり、以下の連絡先を一括で追加します
"山本三郎": {"email": "[email protected]", "phone": "050-1111-2222", "city": "福岡"}
"田中太郎": {"email": "[email protected]", "phone": "090-7777-6666", "city": "札幌"}
("田中太郎"
はすでに存在するので更新されます)
- 更新後のすべての連絡先情報を表示してください
解答例
商品の在庫を管理するシステムを作成しましょう。辞書を使用して商品の情報(名前、価格、在庫数)を管理します。
-
以下の初期商品データを持つ
inventory
辞書を作成してください:- “A001”: {“name”: “ノートパソコン”, “price”: 85000, “stock”: 10}
- “A002”: {“name”: “スマートフォン”, “price”: 60000, “stock”: 15}
- “A003”: {“name”: “タブレット”, “price”: 45000, “stock”: 8}
-
新商品 “A004”(名前: “ワイヤレスイヤホン”, 価格: 15000, 在庫: 20)を追加してください
-
商品 “A002” の価格を55000に、在庫を20に更新してください
-
商品 “A005” が在庫に存在するかどうかを確認し、結果を表示してください
-
商品 “A003” の情報を取り出して削除し、その情報を表示してください
解答例
Python 3.7以降では、辞書はキーの挿入順序を維持するようになりました。
以前のバージョンでは、辞書の要素の順序は保証されていませんでした。
Pythonの辞書はJSON(JavaScript Object Notation)形式と非常に似ています。JSONはデータ交換形式として広く使用されており、Pythonでは標準ライブラリのjson
モジュールを使ってJSONと辞書の相互変換が簡単にできます(詳細は後ほど学習します)。
このため、APIやファイルからJSONデータを取得した場合、それをPythonの辞書として扱うことが一般的です。
リストと同様に、辞書の操作についても計算量を見てみましょう。
Pythonの辞書はハッシュテーブルという仕組みを使っているため、大半の操作が非常に高速です。
特に、キーを使った要素へのアクセスはリストのインデックスアクセスと同様に高速であり、
データ量が増えてもほぼ一定時間で処理できます。
※ 表中の計算量の意味:
O(1)
:入力サイズに関係なく、一定時間で処理されるO(n)
:最悪の場合、辞書内の要素数n
に比例した時間がかかるO(k)
:最悪の場合、2つ目の入力辞書の要素数k
に比例した時間がかかる
操作 | 計算量 | 説明 |
---|---|---|
dict[key] (要素取得) |
O(1)(平均) | 通常は定数時間だが、多数のハッシュ衝突が発生した場合は O(n) になる可能性がある |
dict[key] = x (要素追加・更新) |
O(1)(平均) | 通常は定数時間だが、サイズ拡張時のリハッシュが必要な場合は O(n) になることがある |
key in dict (キー存在確認) |
O(1)(平均) | 通常は定数時間だが、多数のハッシュ衝突が発生した場合は O(n) になる可能性がある |
len(dict) |
O(1) | 辞書のサイズは内部カウンタで保持されているため常に定数時間 |
.get(key, default) |
O(1)(平均) | dict[key] と同様、通常は定数時間だが、最悪の場合は O(n) になる可能性がある |
.pop(key, default) |
O(1)(平均) | キーの検索と削除は通常定数時間だが、ハッシュ衝突の場合は O(n) になる可能性がある |
.update(dict2) |
O(k)(平均) | 通常は追加する辞書の要素数 k に比例するが、リハッシュが必要な場合は O(n+k) になる |
.keys() , .values() , .items() |
O(1) | ビューオブジェクトの生成のみで内部データ構造は複製しない |
list(dict.keys()) など |
O(n) | ビューからリストへの変換は全要素をコピーするため、要素数に比例 |
.copy() |
O(n) | 全要素のシャローコピーを行うため、要素数に比例 |