コンテンツ

Python速習ガイド - ステップ1 基本的な文法とデータ型 (9) - 辞書

Info
  • 本講座は、Pythonプログラミングの基礎を手を動かしながら最速で身につけるための講座です。
  • 「スタイルガイド」では、Pythonできれいなコードを書くためのガイドライン(PEP8)で紹介されている内容を主に記載しています。
  • 各コードは実行して結果を確認することができます。
    ページの再読み込みで元の内容に戻りますので、自由にいじってみてください。

「ステップ1 基本的な文法とデータ型」の続きです。

データをまとめるデータ型として、リストやタプルとは異なるアプローチの 辞書 (dict) があります。
辞書は「キー(key)」と「値(value)」のペアを保持するデータ型で、実際の辞書の「見出し語」と「説明」の関係に似ています。

  • 辞書は波括弧({, })内に キー: 値 のペアをカンマ(,)区切りで並べて作成します。
  • 空の辞書は {} で作成します。
  • dict関数を使用して辞書を作成することもできます。
スタイルガイド
  • コロン(:)の後にはスペースを1個分空けましょう。
  • カンマ(,)の後にはスペースを1個分空けましょう。
  • 長い辞書の場合は複数行に分けると読みやすくなります。
    • 各要素は行の先頭に スペース4個分 の字下げ(インデント)を追加します。
    • 最後の要素の後ろにもカンマを入れましょう。
📚練習問題

以下の商品情報を持つ辞書を作成してみましょう。

  • 商品名(product): 食パン
  • 価格(price): 250
  • 賞味期限(expiration_date): “2025年6月8日” (文字列として入力してください)
  • 在庫数(stock): 15
  • 新商品かどうか(is_new): False

問題なく作成できたら、dict関数を使って同じ辞書を作成してみましょう。

辞書のキーには イミュータブルな (変更不可能な)オブジェクトが使用できます。

  • 数値、文字列、(内部にミュータブルな要素を持たない)タプル、ブール値などはイミュータブルなため、キーとして使用可能です。
  • リストや辞書はキーとして使用できません。
    • ミュータブルな (変更可能な)オブジェクトでは、値が変更されてしまうとキーとしての役割を果たせなくなるため、基本的に使用できません。(詳しくはコラムを参照)
コラム:辞書のキーとハッシュ化可能

Pythonの辞書はキーを使って値を素早く検索するために「ハッシュ」と呼ばれる技術を使っています。
これはキーに対して特別な「ハッシュ値」という数値を計算して、その値をもとに検索するというものです。

このハッシュ値が計算できるオブジェクトのことを「ハッシュ化可能(hashable)」といいます。
ハッシュ化可能かどうかが辞書のキーとして使えるかどうかの基準になります。

基本的には以下のような特徴があります:

  1. イミュータブル(変更不可能)なオブジェクトは通常ハッシュ化可能です
  2. ミュータブル(変更可能)なオブジェクトは通常ハッシュ化不可能です

ミュータブルなオブジェクトがハッシュ化不可能なのは、内容が変わるとハッシュ値も変わってしまうため、辞書の検索システムが正しく機能しなくなるためです。

簡単にまとめると:

  • ハッシュ化可能(辞書のキーに使える): 文字列、数値、タプル(中身がすべてイミュータブルの場合)、None、ブール値など
  • ハッシュ化不可能(辞書のキーに使えない): リスト、辞書など

技術的には、ハッシュ値を適切に定義すればミュータブルなオブジェクトもキーとして使用可能ですが、
そのような使い方は一般的ではなく、データの整合性を保つのが難しくなります。
この点は上級者向けのため、まずは「イミュータブルなものがキーに使える」と覚えておけば十分です。

より詳しい内容は、後に学ぶオブジェクト指向プログラミングの中で理解できるようになります。

  • 辞書の要素はキーを指定して取得します。
  • キーが存在しない場合は KeyError が発生します。
  • 新しいキーと値のペアを追加するには、単にキーを指定して値を代入します。
  • 既存のキーを指定すると、その値が更新されます。
  • キーが辞書に存在するかどうかを in 演算子で確認できます。
  • get メソッドを使うと、キーが存在しない場合にエラーではなくデフォルト値を返すことができます。
    • デフォルト値を指定しない場合は None が返ります。
  • 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 メソッドを使うと、別の辞書の内容で現在の辞書を更新できます。
  • 同じキーがある場合は値が上書きされ、新しいキーは追加されます。
  • pop メソッドはキーを指定して要素を取り出し、辞書から削除します。
    • 戻り値として、指定したキーに対応する値(value)が返ります。
    • キーが存在しない場合のデフォルト値を2つ目の引数で指定できます。
    • デフォルト値を指定せず、キーが存在しない場合は KeyError が発生します。

リストと同様に、辞書にも浅いコピーを行うcopyメソッドがあります。

浅いコピー(シャローコピー)であるため、リストと同様に内側の要素はコピーされないことに注意してください。

  • 深いコピーを行いたい場合はリストと同様に、copyモジュールのdeepcopy関数を使用します。
  • 辞書にbool関数を適用すると、空の辞書({})の場合にFalse、それ以外にTrueを返します。
辞書とタプルとリストの使い分け

これまで学んだデータ型は、それぞれ異なる特徴と適した用途があります。

  • 辞書 (dict) を使うのが適切な場合:

    • キーと値のペアでデータを関連付ける必要がある場合
    • 名前やIDなどで素早くデータを検索したい場合
    • データに意味のあるラベルをつけたい場合
  • タプル (tuple) を使うのが適切な場合:

    • 変更されない固定のデータの集まりを扱う場合
    • 複数の値をグループ化したいが、後で変更する必要がない場合
    • 辞書のキーとして使用する場合
  • リスト (list) を使うのが適切な場合:

    • データの順序が重要な場合
    • 要素を追加、削除、変更する必要がある場合
    • 同じ型の複数の要素を集めたい場合

それぞれの特性を理解し、状況に応じて適切なデータ型を選択することが重要です。

ここまで見てきた辞書に関する操作について、練習問題を解いてみましょう。

📚練習問題1: 辞書の基本操作

商品と価格を管理する辞書に対して、以下の操作を行いましょう。

  1. menuという辞書を作成し、以下の商品と価格を登録してください
    • “コーヒー”: 300
    • “紅茶”: 280
    • “サンドイッチ”: 350
    • “パスタ”: 600
  2. “サラダ”(500円)を追加してください
  3. “コーヒー"の価格を320円に更新してください
  4. メニューに"ケーキ"があるか確認し、結果を表示してください
  5. “オムライス"の価格を表示してください。メニューにない場合は"取り扱いなし"と表示してください(getメソッドを使用)
  6. “パスタ"をメニューから削除し、価格を表示してください
  7. 商品名と価格のペアのリストを表示してください
解答例
📚練習問題2: 住所録の管理

連絡先を管理する住所録を辞書を使って実装しましょう。

  1. contactsという空の辞書を作成してください
  2. 以下の連絡先情報を追加してください
    • "田中太郎": {"email": "[email protected]", "phone": "090-1234-5678", "city": "東京"}
    • "鈴木花子": {"email": "[email protected]", "phone": "080-8765-4321", "city": "大阪"}
  3. "佐藤次郎"を追加し、emailは"[email protected]"、phoneは"070-2222-3333"、cityは"名古屋"としてください
  4. "田中太郎"の電話番号を"090-9999-8888"に更新してください
  5. "鈴木花子"の連絡先をすべて表示してください
  6. すべての連絡先の名前を表示してください(keysメソッドを使用)
  7. 昔の電話番号帳が見つかり、以下の連絡先を一括で追加します
    • "山本三郎": {"email": "[email protected]", "phone": "050-1111-2222", "city": "福岡"}
    • "田中太郎": {"email": "[email protected]", "phone": "090-7777-6666", "city": "札幌"}
      ("田中太郎"はすでに存在するので更新されます)
  8. 更新後のすべての連絡先情報を表示してください
解答例
📚練習問題3: 在庫管理システム

商品の在庫を管理するシステムを作成しましょう。辞書を使用して商品の情報(名前、価格、在庫数)を管理します。

  1. 以下の初期商品データを持つinventory辞書を作成してください:

    • “A001”: {“name”: “ノートパソコン”, “price”: 85000, “stock”: 10}
    • “A002”: {“name”: “スマートフォン”, “price”: 60000, “stock”: 15}
    • “A003”: {“name”: “タブレット”, “price”: 45000, “stock”: 8}
  2. 新商品 “A004”(名前: “ワイヤレスイヤホン”, 価格: 15000, 在庫: 20)を追加してください

  3. 商品 “A002” の価格を55000に、在庫を20に更新してください

  4. 商品 “A005” が在庫に存在するかどうかを確認し、結果を表示してください

  5. 商品 “A003” の情報を取り出して削除し、その情報を表示してください

解答例
辞書の順序保持

Python 3.7以降では、辞書はキーの挿入順序を維持するようになりました。
以前のバージョンでは、辞書の要素の順序は保証されていませんでした。

辞書とJSON形式

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) 全要素のシャローコピーを行うため、要素数に比例

関連記事