9. 辞書#

9.1. 基本#

辞書は、あるオブジェクトを別のオブジェクトに対応付けるデータ構造(連想配列)である。 リストでは、添字(インデックス)に(要素数の範囲内の)整数値しか使えないが、辞書では添字に文字列などのオブジェクトを使うことができる(正確にはタプルなどの不変なオブジェクトを添字として使うことができる)。 Pythonの辞書オブジェクトはハッシュ表として実装されている。

辞書において、あるオブジェクトkが別のオブジェクトvに対応付けられているとき、kをキー(key)、vを値(value)と呼ぶ。まず、以下の表で示される対応を保持する辞書オブジェクトを作成する。

キー (key)

値 (value)

’東京’

'Tokyo'

’神奈川’

'Kanagawa'

’千葉’

'Chiba'

’埼玉’

'Saitama'

d = {'東京': 'Tokyo', '神奈川': 'Kanagawa', '千葉': 'Chiba', '埼玉': 'Saitama'}
d
{'東京': 'Tokyo', '神奈川': 'Kanagawa', '千葉': 'Chiba', '埼玉': 'Saitama'}
type(d)
dict

dict関数を用いて辞書を作成することもできる。

d = dict(東京='Tokyo', 神奈川='Kanagawa', 千葉='Chiba', 埼玉='Saitama')
d
{'東京': 'Tokyo', '神奈川': 'Kanagawa', '千葉': 'Chiba', '埼玉': 'Saitama'}

「神奈川」をキー(添字)として、対応する値を取得する。

d['神奈川']
'Kanagawa'

辞書オブジェクトが格納している対応の数を取得する。

len(d)
4

格納されていないキーで辞書を引こうとするとエラーになる。

d['群馬']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/tmp/ipykernel_205023/3420302070.py in <module>
----> 1 d['群馬']

KeyError: '群馬'

辞書にキーが登録されているかどうか調べるには、in演算子を用いる。

'東京' in d
True
'群馬' in d
False

「群馬」をキーとして値との対応を追加すると、「群馬」で辞書を引けるようになる。

d['群馬'] = 'Gunma'
d['群馬']
'Gunma'
'群馬' in d
True

辞書を引くとき、キーが辞書に登録されていなくてもエラーが出ないようにするには、getメソッドを使う。

d.get('東京')
'Tokyo'

getメソッドは辞書に登録されていないキーが渡されると、Noneを返す。

d.get('栃木')
type(d.get('栃木'))
NoneType

返された値がNoneかどうかを調べる場合は、is演算子を用いる。

d.get('栃木') is None
True

辞書に登録されていないキーが渡されたとき、特定のオブジェクトを返すようにするにはgetメソッドの第2引数に指定する。

d.get('栃木', '辞書に含まれていない都道府県')
'辞書に含まれていない都道府県'

辞書に登録されている対応を削除するには、delキーワードを用いる。

del d['群馬']
'群馬' in d
False

辞書に登録されている全てのキーと値に対して繰り返し処理を行うには、items関数を用いる。

for ja, en in d.items():
    print(ja, en)
東京 Tokyo
神奈川 Kanagawa
千葉 Chiba
埼玉 Saitama

繰り返し処理の中で、辞書の値を更新することもできる。

for ja, en in d.items():
    d[ja] = en.lower()
d
{'東京': 'tokyo', '神奈川': 'kanagawa', '千葉': 'chiba', '埼玉': 'saitama'}

空の辞書は{}かdict関数で作成する。

d = {}
d
{}
d = dict()
d
{}

9.2. タプルをキーとする辞書#

不変なオブジェクトであれば、辞書のキーとして登録できる。数値や文字列はもちろんのこと、複数のオブジェクトをまとめたタプルでもよい。以下のコードでは、2つの数値のタプルをキー、その数値の積を値とした辞書を構築している。

m = {}
for i in range(10):
    for j in range(10):
        m[i,j] = i * j
m[3,4]
12

9.3. ネストした辞書#

辞書の値として辞書オブジェクトを格納すると、2次元配列のように振る舞う辞書オブジェクトを作ることができる。

m = {}
for i in range(10):
    m[i] = {}
    for j in range(10):
        m[i][j] = i * j
m
{0: {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0},
 1: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9},
 2: {0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18},
 3: {0: 0, 1: 3, 2: 6, 3: 9, 4: 12, 5: 15, 6: 18, 7: 21, 8: 24, 9: 27},
 4: {0: 0, 1: 4, 2: 8, 3: 12, 4: 16, 5: 20, 6: 24, 7: 28, 8: 32, 9: 36},
 5: {0: 0, 1: 5, 2: 10, 3: 15, 4: 20, 5: 25, 6: 30, 7: 35, 8: 40, 9: 45},
 6: {0: 0, 1: 6, 2: 12, 3: 18, 4: 24, 5: 30, 6: 36, 7: 42, 8: 48, 9: 54},
 7: {0: 0, 1: 7, 2: 14, 3: 21, 4: 28, 5: 35, 6: 42, 7: 49, 8: 56, 9: 63},
 8: {0: 0, 1: 8, 2: 16, 3: 24, 4: 32, 5: 40, 6: 48, 7: 56, 8: 64, 9: 72},
 9: {0: 0, 1: 9, 2: 18, 3: 27, 4: 36, 5: 45, 6: 54, 7: 63, 8: 72, 9: 81}}
m[3][4]
12

なお、以下のコードはエラーとなる(m[i]を辞書オブジェクトとして初期化していないため)。

m = {}
for i in range(10):
    for j in range(10):
        m[i][j] = i * j
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/tmp/ipykernel_205023/3877937321.py in <module>
      2 for i in range(10):
      3     for j in range(10):
----> 4         m[i][j] = i * j

KeyError: 0

余談ではあるが、collectionsモジュールのdefaultdictのオブジェクトを用いると、辞書の値へのアクセスがあったときに自動的に初期化してくれる。

import collections
m = collections.defaultdict(dict)
for i in range(10):
    for j in range(10):
        m[i][j] = i * j
m[7][4]
28

辞書の値としてリストオブジェクトを格納することもできる。m[i]で返される値がリストオブジェクトであるため、やはり2次元配列のように振る舞う。

m = {}
for i in range(10):
    m[i] = [i * j for j in range(10)]
m
{0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
 2: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18],
 3: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
 4: [0, 4, 8, 12, 16, 20, 24, 28, 32, 36],
 5: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45],
 6: [0, 6, 12, 18, 24, 30, 36, 42, 48, 54],
 7: [0, 7, 14, 21, 28, 35, 42, 49, 56, 63],
 8: [0, 8, 16, 24, 32, 40, 48, 56, 64, 72],
 9: [0, 9, 18, 27, 36, 45, 54, 63, 72, 81]}
m[5][7]
35

9.4. 辞書の内包表記#

九九の2の段を表現する辞書オブジェクトを作成してみる。

m2 = {}
for i in range(10):
    m2[i] = i * 2
m2
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

このオブジェクトは、次のように辞書の内包表記で構築することもできる。「i corresponds to i times 2 for all i in the range of ten」のように英語読みすると分かりやすいかもしれない。

m2n = {i: i*2 for i in range(10)}
m2n
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

先ほどは2の段だけであったが、これを0から9の段まで繰り返すと、九九の表ができあがる。

m = {}
for i in range(10):
    m[i] = {j: i*j for j in range(10)}
m
{0: {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0},
 1: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9},
 2: {0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18},
 3: {0: 0, 1: 3, 2: 6, 3: 9, 4: 12, 5: 15, 6: 18, 7: 21, 8: 24, 9: 27},
 4: {0: 0, 1: 4, 2: 8, 3: 12, 4: 16, 5: 20, 6: 24, 7: 28, 8: 32, 9: 36},
 5: {0: 0, 1: 5, 2: 10, 3: 15, 4: 20, 5: 25, 6: 30, 7: 35, 8: 40, 9: 45},
 6: {0: 0, 1: 6, 2: 12, 3: 18, 4: 24, 5: 30, 6: 36, 7: 42, 8: 48, 9: 54},
 7: {0: 0, 1: 7, 2: 14, 3: 21, 4: 28, 5: 35, 6: 42, 7: 49, 8: 56, 9: 63},
 8: {0: 0, 1: 8, 2: 16, 3: 24, 4: 32, 5: 40, 6: 48, 7: 56, 8: 64, 9: 72},
 9: {0: 0, 1: 9, 2: 18, 3: 27, 4: 36, 5: 45, 6: 54, 7: 63, 8: 72, 9: 81}}
m[3][4]
12

以下のコードは、数値のタプルをキーとする辞書オブジェクトで九九の表を構築するコードである。

m = {}
for i in range(10):
    for j in range(10):
        m[i,j] = i*j
m[3,4]
12

この辞書オブジェクトは、以下のように内包表記をネストさせることで1行で構築できる。

m = {(i,j): i*j for j in range(10) for i in range(10)}
m[3,4]
12

9.5. ネストしたオブジェクトの構築#

辞書を定義する{ }と、リストを定義する[ ]を一緒に使ってもよい。以下のコードでは、都道府県の漢字をキーとして、その都道府県の読み方と英語表記をリストとしてまとめたものを値とする辞書を構築している。

D = {
    '東京': ['とうきょう', 'Tokyo'],
    '神奈川': ['かながわ', 'Kanagawa'],
    '千葉': ['ちば', 'Chiba'],
    '埼玉': ['さいたま', 'Saitama']
}
D['神奈川']
['かながわ', 'Kanagawa']
D['神奈川'][0]
'かながわ'
D['神奈川'][1]
'Kanagawa'

なお、このようなネストした辞書に対する繰り返し処理の書き方は色々ありうる。以下のコードでは、辞書のキー(都道府県の漢字)をpref、値をvalueという変数で受け取り、表示するときに読み方をvalue[0]、英語表記をvalue[1]としてアクセスしている。

for pref, value in D.items():
    print(f'{pref}: 読み方={value[0]}, 英語={value[1]}')
東京: 読み方=とうきょう, 英語=Tokyo
神奈川: 読み方=かながわ, 英語=Kanagawa
千葉: 読み方=ちば, 英語=Chiba
埼玉: 読み方=さいたま, 英語=Saitama

以下のコードでは、辞書のキーをpref、値を(yomi, en)というタプルでアンパックすることで、都道府県の漢字をpref、読み方をyomi、英語表記をenという変数でアクセスしている。

for pref, (yomi, en) in D.items():
    print(f'{pref}: 読み方={yomi}, 英語={en}')
東京: 読み方=とうきょう, 英語=Tokyo
神奈川: 読み方=かながわ, 英語=Kanagawa
千葉: 読み方=ちば, 英語=Chiba
埼玉: 読み方=さいたま, 英語=Saitama

辞書を定義する{ }をネストさせてもよい。以下のコードでは、都道府県の漢字をキーとして、その都道府県の読み方と英語表記を辞書としてまとめたものを値とする辞書を構築している。

D = {
    '東京': {'yomi': 'とうきょう', 'en': 'Tokyo'},
    '神奈川': {'yomi': 'かながわ', 'en': 'Kanagawa'},
    '千葉': {'yomi': 'ちば', 'en': 'Chiba'},
    '埼玉': {'yomi': 'さいたま', 'en': 'Saitama'}
}
D['神奈川']
{'yomi': 'かながわ', 'en': 'Kanagawa'}
D['神奈川']['yomi']
'かながわ'
D['神奈川']['en']
'Kanagawa'

なお、先ほどのように辞書の中身を表示するコードは以下のようになる(シングルクォーテーション(')で記述している文字列の内側において、辞書の内側のキーにアクセスするためにシングルクォーテーションが使いづらいので、ダブルクォーテーション(")を使っている)

for pref, value in D.items():
    print(f'{pref}: 読み方={value["yomi"]}, 英語={value["en"]}')
東京: 読み方=とうきょう, 英語=Tokyo
神奈川: 読み方=かながわ, 英語=Kanagawa
千葉: 読み方=ちば, 英語=Chiba
埼玉: 読み方=さいたま, 英語=Saitama