Python基礎講座 #10 辞書 (Dictionary)

読了 10分

辞書はリストとともに、Pythonで最もよく使われるデータ構造の一つです。辞書の最大の特徴は、key、valueマッピングを使うデータ構造であるという点です。そしてstring、list、tupleなどのシーケンス (sequence) データ型とは違って、順序のないデータ型です。これらの特徴については、後で詳しく見ていきましょう。

まず辞書を定義する方法を見ていきましょう。辞書はカーリーブレースを使って定義します。カーリーブレースを使って空の辞書を定義してみましょう。

>>> my_dict = {}
>>> my_dict
{}

定義したオブジェクトのデータ型を確認してみましょう。

>>> type(my_dict)
<class 'dict'>

辞書が定義されたことが分かります。辞書のアイテムはkey、value形式を持ち、コロンで区切られます。そしてアイテムとアイテムの間はカンマで区切られます。

カーリーブレースを使って簡単な辞書を定義してみましょう。

>>> person = {
...     'first_name': 'Sanghee',
...     'last_name': 'Lee',
...     'age': 25
... }
>>> person
{'first_name': 'Sanghee', 'last_name': 'Lee', 'age': 25}

辞書を定義するもう一つの方法は、クラスコンストラクタを使うことです。コンストラクタを使って辞書を定義してみましょう。

>>> person_2 = dict(
...     first_name='Sanghee',
...     last_name='Lee',
...     age=25
... )
>>> person_2
{'first_name': 'Sanghee', 'last_name': 'Lee', 'age': 25}

コンストラクタで定義する時はkeyとvalueの間にコロンを使うのではなく、変数にデータを代入するように、イコール記号を使います。そしてキーが変数と同じ役割をするため、クォートで囲んではいけません。もしキーをクォートで囲むと次のようなシンタックスエラーが発生します。

>>> my_dict = dict('key'='value')
File "<stdin>", line 1
my_dict = dict('key'='value')
^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?

次のようにイコール記号の代わりにコロンを使ってもシンタックスエラーが発生します。

>>> my_dict = dict(key: 'value')
File "<stdin>", line 1
my_dict = dict(key: 'value')
^
SyntaxError: invalid syntax

💡 今日のヒント

辞書を定義する時に注意点が一つあるのですが、辞書を保存する変数の名前として組み込みクラス名であるdictを使うと、dictの中に定義された型オブジェクトが辞書オブジェクトでオーバーライトされてしまい、辞書コンストラクタが使えなくなりますので、注意してください。

辞書のアイテムデータを参照したり変更したい時は、各アイテムのkeyを使えば大丈夫です。ageキーを使って25というデータを出力してみましょう。

>>> person['age']
25

今度はageデータを変更してみましょう。

>>> person['age'] = 30
>>> person
{'first_name': 'Sanghee', 'last_name': 'Lee', 'age': 30}

age valueが30に変更されたのが分かります。新しいアイテムを追加したり、既存のアイテムを削除することもできます。

アイテムを一つ追加します。

>>> person['job'] = 'programmer'
>>> person
{'first_name': 'Sanghee', 'last_name': 'Lee', 'age': 30, 'job': 'programmer'}

del組み込み関数を使って、追加したアイテムを削除します。

>>> del person['job']
>>> person
{'first_name': 'Sanghee', 'last_name': 'Lee', 'age': 30}

追加したアイテムが削除されたのが分かります。

辞書のデータを参照したり削除する時、辞書の中に存在しないkeyを使うと、keyエラーが発生するので注意してください。

存在しないkeyを使ってデータを参照してみましょう。

>>> person['address']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'address'

今度は存在しないkeyを使ってデータを削除してみましょう。

>>> del person['address']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'address'

辞書のkeyとして使えるデータ型はint、float、str、tupleです。

各データ型をkeyとして使ってみましょう。

>>> my_dict = {
...     1: 'int',
...     1.23: 'float',
...     '123': 'string',
...     (1, 2, 3): 'tuple'
... }
>>> my_dict
{1: 'int', 1.23: 'float', '123': 'string', (1, 2, 3): 'tuple'}

ただし、リストや辞書型はkeyとして使えません。リストをkeyとして使ってみましょう。

>>> d = {[1, 2, 3]: 'list'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list

今度は辞書をkeyとして使ってみましょう。

>>> d = {{1: 1}: 'dict'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

今度は辞書のvalueにどんなデータを保存できるか見ていきましょう。

辞書のvalueには、リストの中にすべてのデータ型を保存できたのと同じく、すべてのデータ型を保存することができます。

コードを実行して確認してみましょう。

>>> my_dict = {
...     'int': 1,
...     'float': 1.23,
...     'str': 'string',
...     'list': [1, 2, 3],
...     'tuple': (1, 2, 3),
...     'dict': {'one': 1, 'two': 2}
... }
>>> my_dict
{'int': 1, 'float': 1.23, 'str': 'string', 'list': [1, 2, 3], 'tuple': (1, 2, 3), 'dict': {'one': 1, 'two': 2}}

すべてのデータ型が保存されたのが確認できました。

辞書の中に保存されたリストのアイテムや辞書のアイテムにアクセスする時は、“my_dict[‘key1’][‘key2’]” のようなシンタックスを使えば大丈夫です。

リストアイテムと辞書のアイテムのvalueを参照してみましょう。

>>> my_dict['list'][0]
1
>>> my_dict['dict']['one']
1

先ほど辞書は順序のないデータと申し上げました。リストのようなシーケンスデータ型は、定義された順序通りにデータが保存されます。常に同じ順序を保つので、インデックスを使うことができ、ループを使って常に同じ順序でデータにアクセスすることができます。しかし辞書はそうではありません。アイテムの順序が保証されず、新しいアイテムを追加しても最後の位置ではなく別の位置に保存される可能性があります。しかしPython 3.7バージョンからは、辞書もリストと同じく定義された順序を覚え、新しいアイテムを追加した時に最後の位置に保存されるようになりました。

まず3.7未満のバージョンの辞書を確認してみましょう。まずPythonバージョンを確認してみましょう。

>>> import sys
>>> print(sys.version)
3.5.6 |Anaconda, Inc.| (default, Aug 26 2018, 16:05:27) [MSC v.1900 64 bit (AMD64)]

バージョンが3.5.6であることを確認しました。

辞書を一つ作成した後、辞書に保存されたデータをプリントしてみましょう。

>>> my_dict = {
...     'key_1': 1,
...     'key_2': 2,
...     'key_3': 3,
... }
>>> my_dict
{'key_3': 3, 'key_2': 2, 'key_1': 1}

保存した順序と異なって出力されるのが分かります。

今度は新しいアイテムを追加した後、データを出力してみましょう。

>>> my_dict['key_4'] = 4
>>> my_dict
{'key_1': 1, 'key_4': 4, 'key_2': 2, 'key_3': 3}

💡 今日のヒント

Pythonバージョン3.5以下の辞書は順序がないため、新しく追加するアイテムがどの位置に追加されるか分かりません。

追加したアイテムが最後に追加されていないのが分かります。

今度はもう一度3.7以上のバージョンの辞書を確認してみましょう。

>>> import sys
>>> print(sys.version)
3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)]
>>> my_dict = {
...     'key_1': 1,
...     'key_2': 2,
...     'key_3': 3,
... }
>>> my_dict
{'key_1': 1, 'key_2': 2, 'key_3': 3}

アイテムが定義した順序通りに出力されるのが分かります。

今度は新しいアイテムを追加してみましょう。

>>> my_dict['key_4'] = 4
>>> my_dict
{'key_1': 1, 'key_2': 2, 'key_3': 3, 'key_4': 4}

追加したアイテムが最後の位置に保存されたのが分かります。

今度は辞書クラスが持っているクラスメソッドの使い方について見ていきましょう。

まずhelp関数で、辞書クラスの中にどんなメソッドが定義されているか確認してみましょう。

>>> help(dict)
Help on class dict in module builtins:

class dict(object)
|  dict() -> new empty dictionary
|  dict(mapping) -> new dictionary initialized from a mapping object's
|      (key, value) pairs
|  dict(iterable) -> new dictionary initialized as if via:
|      d = {}
|      for k, v in iterable:
|          d[k] = v
|  dict(**kwargs) -> new dictionary initialized with the name=value pairs
|      in the keyword argument list.  For example:  dict(one=1, two=2)
|
|  Methods defined here:
|
|  __contains__(self, key, /)
|      True if the dictionary has the specified key, else False.
|
|  __delitem__(self, key, /)
|      Delete self[key].
|
|  __eq__(self, value, /)
|      Return self==value.
|
|  __ge__(self, value, /)
|      Return self>=value.
|
|  __getattribute__(self, name, /)
|      Return getattr(self, name).
|
|  __getitem__(...)
|      x.__getitem__(y) <==> x[y]
|
|  __gt__(self, value, /)
|      Return self>value.
|
|  __init__(self, /, *args, **kwargs)
|      Initialize self.  See help(type(self)) for accurate signature.
|
|  __ior__(self, value, /)
|      Return self|=value.
|
|  __iter__(self, /)
|      Implement iter(self).
|
|  __le__(self, value, /)
|      Return self<=value.
|
|  __len__(self, /)
|      Return len(self).
|
|  __lt__(self, value, /)
|      Return self<value.
|
|  __ne__(self, value, /)
|      Return self!=value.
|
|  __or__(self, value, /)
|      Return self|value.
|
|  __repr__(self, /)
|      Return repr(self).
|
|  __reversed__(self, /)
|      Return a reverse iterator over the dict keys.
|
|  __ror__(self, value, /)
|      Return value|self.
|
|  __setitem__(self, key, value, /)
|      Set self[key] to value.
|
|  __sizeof__(...)
|      D.__sizeof__() -> size of D in memory, in bytes
|
|  clear(...)
|      D.clear() -> None.  Remove all items from D.
|
|  copy(...)
|      D.copy() -> a shallow copy of D
|
|  get(self, key, default=None, /)
|      Return the value for key if key is in the dictionary, else default.
|
|  items(...)
|      D.items() -> a set-like object providing a view on D's items
|
|  keys(...)
|      D.keys() -> a set-like object providing a view on D's keys
|
|  pop(...)
|      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
|      If key is not found, default is returned if given, otherwise KeyError is raised
|
|  popitem(self, /)
|      Remove and return a (key, value) pair as a 2-tuple.
|
|      Pairs are returned in LIFO (last-in, first-out) order.
|      Raises KeyError if the dict is empty.
|
|  setdefault(self, key, default=None, /)
|      Insert key with a value of default if key is not in the dictionary.
|
|      Return the value for key if key is in the dictionary, else default.
|
|  update(...)
|      D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
|      If E is present and has a .keys() method, then does:  for k in E: D[k] = E[k]
|      If E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v
|      In either case, this is followed by: for k in F:  D[k] = F[k]
|
|  values(...)
|      D.values() -> an object providing a view on D's values
|
|  ----------------------------------------------------------------------
|  Class methods defined here:
|
|  __class_getitem__(...) from builtins.type
|      See PEP 585
|
|  fromkeys(iterable, value=None, /) from builtins.type
|      Create a new dictionary with keys from iterable and values set to value.
|
|  ----------------------------------------------------------------------
|  Static methods defined here:
|
|  __new__(*args, **kwargs) from builtins.type
|      Create and return a new object.  See help(type) for accurate signature.
|
|  ----------------------------------------------------------------------
|  Data and other attributes defined here:
|
|  __hash__ = None

この中で最もよく使われるいくつかのメソッドの使い方について見ていきましょう。

まず辞書に定義されたアイテムを削除するclearメソッドを使ってみましょう。辞書を一つ定義した後、clearメソッドを使ってすべてのアイテムを削除してみましょう。

>>> my_dict = {
...     'one': 1,
...     'two': 2,
...     'three': 3
... }
>>> my_dict
{'one': 1, 'two': 2, 'three': 3}
>>> my_dict.clear()
>>> my_dict
{}

すべてのアイテムが削除されたことが確認できました。

次はgetメソッドです。 getメソッドは、辞書のkeyが存在すればkeyに対するvalueを返し、keyが存在しなければNoneを返します。このメソッドは、存在しないkeyを参照してkeyエラーが発生するのを防ぐためによく使われます。

まず辞書を定義した後、getメソッドを使ってデータを出力してみましょう。

>>> my_dict = {
...     'key': 'value'
... }
>>> my_dict.get('key')
'value'

今度は存在しないkeyをgetメソッドに渡してみましょう。

>>> my_dict.get('no_key')
>>>

Pythonコンソールやjupyter notebookは、返される値がない、またはNoneの場合は何も出力しません。

print関数を使って、本当にNoneが返されているか確認してみましょう。

>>> print(my_dict.get('no_key'))
None

次はitemsメソッドです。 Pythonの辞書はfor loopで使えるイテレータオブジェクトです。 辞書を一つ定義してiter関数で確認してみましょう。

>>> my_dict = {
...     'one': 1,
...     'two': 2,
...     'three': 3
... }
>>>
>>> iter(my_dict)
<dict_keyiterator object at 0x000002136D0E9B30>

イテレータオブジェクトであることが確認できました。

for loopを使ってデータを出力してみましょう。

>>> for i in my_dict.items():
...     print(i)
...
('one', 1)
('two', 2)
('three', 3)

各アイテムが括弧の中に保存されているのを見ると、各アイテムはタプルとして保存されているのが分かります。実際のアイテムのデータ型を確認してみましょう。

>>> for i in my_dict.items():
...     print(type(i))
...
<class 'tuple'>
<class 'tuple'>
<class 'tuple'>

一般的に、itemsメソッドを使ってループする時は、次の例のようにkeyの値を保存する変数とvalueの値を保存する変数を定義した後、それぞれのデータをアンパッキングして使います。

>>> for k, v in my_dict.items():
...     print('key: {}, value: {}'.format(k, v))
...
key: one, value: 1
key: two, value: 2
key: three, value: 3

次はkeysメソッドです。このメソッドは、辞書に定義されたすべてのkeyをリスト形式で返します。 このメソッドは、keyの並べ替えされた順序通りにデータにアクセスしなければならない場合によく使われます。

まず辞書を定義した後、for loopを使ってみましょう。

>>> for i in my_dict:
...     print(my_dict[i])
...
2
1
3

データが順序なく出力されるのが分かります。

今度はキーを並べ替えて順番に出力してみましょう。

>>> my_keys = my_dict.keys()
>>> my_keys
dict_keys(['b', 'a', 'c'])

sorted関数を使って順番に並べ替えてみましょう。

>>> my_keys = sorted(my_keys)
>>> my_keys
['a', 'b', 'c']

並べ替えされたkeyを使ってアイテムを出力してみましょう。

>>> for i in my_keys:
...     print(my_dict[i])
...
1
2
3

数字が順番に出力されました。

次に確認するメソッドはvaluesメソッドです。このメソッドは、辞書のすべてのvalueをリスト形式で返します。

valuesメソッドを使ってすべてのvalueを出力してみましょう。

>>> my_dict.values()
dict_values([2, 1, 3])

次はupdateメソッドです。このメソッドは、二つの辞書を一つに結合してくれるとても便利なメソッドです。

二つの辞書を定義した後、一つの辞書に結合してみましょう。

>>> dict_a = {'key_a': 'value_a'}
>>> dict_b = {'key_b': 'value_b'}
>>> dict_a.update(dict_b)
>>> dict_a
{'key_a': 'value_a', 'key_b': 'value_b'}

二つの辞書が一つに結合されました。

今日の講座はここまでにします。この講座は基礎講座なので、辞書の基礎的な部分のみ説明しましたが、別の講座を通じて、実際にプログラミングをする時に辞書をどのように活用するかについて説明していきます。

次回の講座では、リストや辞書ほどよくは使われませんが、よく知っておくととても便利なセットについて勉強しましょう。

X