Pythonのdictクラスには、辞書を扱うための便利なメソッドがたくさんあります。
その一部は、dictsのコンセプトで紹介しました。
ここでは、さらにいくつかのメソッドを取り上げます。あわせて、辞書を繰り返し処理したり操作したりするためのテクニックも見ていきましょう。
setdefault()を使いましょう辞書のコンセプトでは、.get(key, <default value>)が、辞書にkeyが見つからない場合に既存のvalueまたはdefault valueを返すことを説明しました。これにより、KeyErrorを避けられます。
これは、余分なエラー処理を書きたくないけれど、探しているkeyが必ず存在するとは言い切れない場面で役立ちます。
同じように「安全な」(KeyErrorを出さずに)挿入操作には、.setdefault(key, <default value>)メソッドがあります。
setdefault(key, <default value>)は、keyが辞書に見つかればvalueを返します。
キーが見つからなければ、(key, default value)のペアを_挿入_し、default valueを返します。
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
# Looking for the value associated with key "Rock Brown".
# The key does not exist, so it is added with the default value, and the value is returned.
>>> palette_I.setdefault('Rock Brown', '#694605')
'#694605'
# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
fromkeys()を使いましょうさまざまなkeysとデフォルト値で辞書をすばやく埋めるには、クラスメソッド fromkeys(iterable, <default value>)を使います。これはkeysのイテラブルを繰り返し処理して、新しいdictを作成します。
すべてのvaluesは、指定したdefault valueに設定されます。
>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
{'Grassy Green': 'fill in hex color here',
'Purple Mountains Majesty': 'fill in hex color here',
'Misty Mountain Pink': 'fill in hex color here'}
.keys()、.values()、.items()メソッドは、辞書のイテラブルなビューを返します。
これらのビューを使うと、項目を変更せずに簡単にループできます。
ビューは_動的_でもあります。元になっている辞書のデータが変わると、対応するview objectにもその変更が反映されます。
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
# Views are dynamic. Changing values in the dict changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'
>>> palette_I.values()
dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
.keys()、.values()、.items()をもう少し詳しくPython 3.7以降では、dictsは項目を挿入した順序を保持します。そのため、.keys()、.values()、.items()を使うと、先入れ先出し(FIFO)で繰り返し処理できます。
Python 3.8以降では、ビューを_逆順にたどる_こともできます。
これにより、reversed(<dict>.keys())、reversed(<dict>.values())、reversed(<dict>.items())を使うと、キー、値、または(key, value)のペアを後入れ先出し(LIFO)の順で繰り返し処理できます。
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
# Iterating in insertion order (First in, first out)
>>> for item in palette_II.items():
... print(item)
...
('Factory Stone Purple', '#7c677f')
('Green Treeline', '#478559')
('Purple baseline', '#161748')
# Iterating in the reverse direction. (Last in, first out)
>>> for item in reversed(palette_II.items()):
... print (item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
('Factory Stone Purple', '#7c677f')
辞書には、組み込みの並べ替えメソッドはありません。
ただし、組み込み関数sorted()とdict.items()を使えば、dictの_ビュー_を並べ替えられます。
並べ替えたビューを使って、新しい辞書を作成できます。
繰り返し処理と同様、デフォルトでは辞書のkeysを基準に並べ替えられます。
# Default ordering for a dictionary is insertion order (First in, first out).
>>> color_palette = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
'Grassy Green': '#9bc400',
'Green Treeline': '#478559',
'Misty Mountain Pink': '#f9c5bd',
'Purple Mountains Majesty': '#8076a3',
'Purple baseline': '#161748'}
.update()で辞書を結合しましょう<dict_one>.update(<dict_two>)を使うと、2つの辞書を_結合_できます。
このメソッドは、<dict_two>の(key,value)のペアを取り出して、<dict_one>に書き込みます。
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple Baseline': '#161748'}
>>> palette_I.update(palette_II)
# Note that new items from palette_II are added.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
2つの辞書でキーが_重複_している場合、dict_oneのvalueは、dict_twoの対応するvalueで_上書き_されます。
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)
# Overlapping values in palette_I are replaced with values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
|と|=)で辞書をマージまたは更新しましょうPython 3.9では、dictsをマージする別の方法としてunion演算子が導入されました。
dict_one | dict_twoは、dict_oneとdict_twoの(key, value)のペアからなる新しい辞書を作成します。
両方の辞書に同じキーがある場合、dict_twoの値が優先されます。
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> new_dict = palette_I | palette_II
>>> new_dict
...
{'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
dict_one |= otherは<dict_one>.update(<other>)とよく似た動作をしますが、この場合はotherにdictだけでなく、(key, value)のペアのイテラブルも指定できます。
>>> palette_III = {'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> new_dict |= palette_III
>>> new_dict
...
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
辞書とその扱い方について詳しく知りたい場合は、公式チュートリアルと公式ライブラリリファレンスが最適な出発点です。
Real PythonとFinxterにも、Pythonの辞書に関する非常に詳しい記事があります。
食料品の買い物を自動化する企業、Mecha Munch™に、注文アプリの開発担当として加わることになりました。 チームに課された任務は、ショッピングカートの基本的な操作をすべて管理するMVP(実用最小限の製品)を構築することです。 これにより、ユーザーは食料品の注文を追加・削除・並べ替えできるようになります。 ありがたいことに、お金とチェックアウトの機能は別のチームが担当してくれています!
MVPでは、ユーザーがショッピングカートに商品を追加できるようにします。 一度に1つの商品でも、複数の商品でもかまいません。 MVPなので、商品の個数は_繰り返し_で表します。 たとえばオレンジを2つ追加したいときは、入力のイテラブルに'Oranges'が2回現れます。 その商品がすでにカートに入っている場合は、カートの個数を1増やします。 カートに_新しく_追加する商品は、個数1で登録します。
関数add_item(<current_cart>, <items_to_add>)を作成します。
この関数は、カートの辞書と、追加する商品のリストのようなイテラブルを引数に取ります。
ユーザーの新しい(更新された)ショッピングカートの辞書を返します。
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
{'Banana': 4, 'Apple': 5, 'Orange': 2}
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
['Banana', 'Orange', 'Blueberries', 'Banana'])
{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
おっと。 どうやらプロダクトチームが機能の肥大化に手を出したようです。 MVPに追加の機能を盛り込みたいのだそうです。 アプリは、ユーザーのメモアプリから商品を読み取ってショッピングカートを作れるようになりました。 ユーザーには便利ですが、チームの作業は少し増えます。
関数read_notes(<notes>)を作成します。
この関数は、リストのような任意のイテラブルを引数に取ります。
商品を解析して、ユーザーのショッピングカート(辞書)を作ります。
各商品は個数1で追加します。
そうしてできた新しいユーザーのカートを返します。
>>> read_notes(('Banana','Apple', 'Orange'))
{'Banana': 1, 'Apple': 1, 'Orange': 1}
>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
アプリには、さまざまな料理の完成したレシピが並ぶ「アイデア」セクションがあります。 ユーザーがこれらのレシピを1つ選ぶと、その材料がすべて自動でショッピングカートに追加されます。 コンテンツチームが材料や分量を頻繁に変更するため、プロジェクトマネージャーから、この「アイデア」レシピを編集する方法を作るよう依頼されました。
関数update_recipes(<ideas>, <recipe_updates>)を作成します。
この関数は、「アイデア」の辞書と、レシピの更新内容のイテラブルを引数に取ります。
新しい(更新された)「アイデア」の辞書を返します。
>>>update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
(('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)
)
...
{'Banana Bread': {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
>>> update_recipes(
{'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})]
)
...
{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
ユーザーがカートを作り始めると、アプリは商品をアルファベット順に並べ替えられるようにします。 これで商品を探しやすくなり、'potatoes'と'Potato'がデータベースに混在しているような入力ミスにも役立ちます。
関数sort_entries(<cart>)を作成します。
この関数はショッピングカート(辞書)を引数に取り、アルファベット順に並べ替えた新しいカートを返します。
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
アプリは、あるユーザーのカートを店舗に送り、商品をそろえてもらう必要があります。 ただし、店舗のスタッフは、その商品がどの売り場にあるのか、冷蔵が必要かどうかを知る必要があります。 そこで(かなり勝手な決まりですが)、「フルフィルメントカート」は、商品の個数に売り場と冷蔵の情報を組み合わせたうえで、アルファベットの逆順に並べる必要があります。
関数send_to_store(<cart>, <aisle_mapping>)を作成します。
この関数は、ユーザーのショッピングカートと、商品ごとに売り場番号と冷蔵の要否(True/False)を持つ辞書を引数に取ります。
お客さんが注文する各商品について、(個数・売り場・冷蔵)を持つ統合済みの「フルフィルメントカート」をreturnします。
商品はアルファベットの_逆順_で並びます。
>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
{'Banana': ['Aisle 5', False], 'Apple': ['Aisle 4', False], 'Orange': ['Aisle 4', False], 'Milk': ['Aisle 2', True]})
{'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]}
アプリがいつまでもお客さんの注文を受け付けるわけにはいきません。 いずれ、店舗のさまざまな商品は在庫切れになります。 そのため、MVPのアプリは、ユーザーが注文を店舗に送るたびに店舗の在庫を更新する必要があります。 そうしないと、お客さんが実際には在庫のない商品を注文してしまいます。
関数update_store_inventory(<fulfillment_cart>, <store_inventory>)を作成します。
この関数は、「フルフィルメントカート」と店舗の在庫を引数に取ります。
「フルフィルメントカート」で「注文された」数だけ店舗の在庫を減らし、更新後の店舗の在庫を返します。
店舗の商品の数が0になったところでは、その数を'Out of Stock'というメッセージに置き換えます。
>>> update_store_inventory({'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]},
{'Banana': [15, 'Aisle 5', False], 'Apple': [12, 'Aisle 4', False], 'Orange': [1, 'Aisle 4', False], 'Milk': [4, 'Aisle 2', True]})
{'Banana': [12, 'Aisle 5', False], 'Apple': [10, 'Aisle 4', False], 'Orange': ['Out of Stock', 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True]}