Pythonの便利技と注意点:辞書編

[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)

# strが入ってる辞書を打鍵数を少なくして作成する方法
```Py:キーはstrを''で囲まなくても辞書を作成してくれる
menu = dict(ハンバーガー='焼く', ポテト='揚げる')
print(menu)
==================================================
# 実行結果
{'ハンバーガー': '焼く', 'ポテト': '揚げる'}
```

```Py:値は''で囲まないとエラーになる
menu = dict(ハンバーガー=焼く, ポテト=揚げる)
print(menu)
===============================================
# 実行結果
    menu = dict(ハンバーガー=焼く, ポテト=揚げる)
                       ^^
NameError: name '焼く' is not defined
```

# items()でkeyとvalue両方をとってくる
```Py:
lunch_menu = {
    'Mon': 'Hamburger',
    'Tue': 'Curry',
    'Wed': 'Sushi',
    'Thu': 'Ramen',
    'Fri': 'Beef steak'
}

for week_day, menu in lunch_menu.items():
    print(week_day, ':', menu)
```

```Py:出力結果
Mon : Hamburger
Tue : Curry
Wed : Sushi
Thu : Ramen
Fri : Beef steak
```

# 辞書のcopy()とdeepcopy()
```py:copyは新しい値を返す為、copy先の値を変えても、copy元は変えない
>>> dicA = {'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}
>>> dicB = dicA.copy()
>>> dicB
{'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}

>>> dicB['名前'] = '次郎'
>>> dicB
{'名前': '次郎', '備品': ['PC', '携帯', 'カバン']}
>>> dicA
{'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}
```
```py:値がオブジェクトの場合は、copy元を参照する為、copy先を変えるとcopy元も変わる
>>> dicA = {'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}
>>> dicB = dicA.copy()

>>> dicB['備品'].remove('携帯')

>>> dicB
{'名前': '太郎', '備品': ['PC', 'カバン']}
>>> dicA
{'名前': '太郎', '備品': ['PC', 'カバン']}
```
```py:deepcopy()を使うと、オブジェクトも全て新しい値を作り出す為、copy先のオブジェクト変更がcopy元に影響しない
>>> from copy import deepcopy
>>> dicA = {'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}
>>> dicC = deepcopy(dicA)

>>> dicC['名前'] = '三郎'
>>> dicC
{'名前': '三郎', '備品': ['PC', '携帯', 'カバン']}
>>> dicA
{'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}

>>> dicC['備品'].remove('携帯')
>>> dicC
{'名前': '三郎', '備品': ['PC', 'カバン']}
>>> dicA
{'名前': '太郎', '備品': ['PC', '携帯', 'カバン']}
```

# 辞書入りプログラムで存在しない項目にアクセスした時にpythonエラーではなくNoneを返してくれるget()
```py:辞書にない項目にアクセスすると通常エラーが返る
>>> dicZ = {}
>>> print(dicZ['name'])
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    print(dicZ['name'])
KeyError: 'name'
```
```py:get()を使うことで項目がないとNoneと返してくれる
>>> dicZ = {}
>>> print(dicZ.get('name'))
None
```

```py:第二引数にデフォルト値を設定することで、その値を表示することも可能
>>> dicZ = {}
>>> dicZ.get('name', 'ありません')
'ありません'
```

# リストにあるkeyに対応する値を辞書からとってくる
```py:辞書.get(key)
>>> dic = {'A': 1, 'B':2, 'C':3, 'D':4}
>>> key_list = ['B', 'C', 'D', 'A']
>>> for key in key_list:
...     print(key, ':', dic.get(key))
... 
B : 2
C : 3
D : 4
A : 1
```


# 各文字がいくつあるのか数えて辞書形式で出力する
```Py:新たな文字が出てきた時に0をセットしてfor文で回す方法
>>> strs = "jjjssssaawoa;slblskwpalswa;cbx"
>>> d = {}
>>> for c in strs:
...     if c not in d:
...             d[c] = 0
...     d[c] += 1
... 
>>> print(d)
{'j': 3, 's': 7, 'a': 5, 'w': 3, 'o': 1, ';': 2, 'l': 3, 'b': 2, 'k': 1, 'p': 1,
 'c': 1, 'x': 1}
```

```Py:setdefault()関数を使う方法
>>> strs = "jjjssssaawoa;slblskwpalswa;cbx"
>>> d = {}
>>> for c in strs:
...     d.setdefault(c, 0)
...     d[c] += 1
... 
>>> print(d)
{'j': 3, 's': 7, 'a': 5, 'w': 3, 'o': 1, ';': 2, 'l': 3, 'b': 2, 'k': 1, 'p': 1,
 'c': 1, 'x': 1}
```

# 辞書のupdate()
```Py:既存のキーは更新、存在しないキーは追加となる
set_menu_order = {'ハンバーガー': 250, 'ポテトS': 150}
modified_set_menu_order = {'ハンバーガー': 300, 'コーラS': 100}

set_menu_order.update(modified_set_menu_order)
print(set_menu_order)
```
```Py:出力結果
{'ハンバーガー': 300, 'ポテトS': 150, 'コーラS': 100}
```

# 辞書形式でCSVファイルに出力する
```Py:
# csvパッケージをインポート
import csv

# 列名設定
fieldnames = ['Menu', 'Price']

# ファイル書き込み開始
# ※１行ずつ空いてしまう場合、newline=''オプションをつける
with open('example.csv', 'w', newline='') as f:

    # csvパッケージのDictWriter()関数を呼び出し、ファイル名と列名を指定
    writer = csv.DictWriter(f, fieldnames)

    # 最初に列を書く
    writer.writeheader()

    # writerow()関数を使って、辞書形式で{'列名': '値'}を書き込む
    writer.writerow({'Menu': 'ハンバーガー', 'Price': 300})
    writer.writerow({'Menu': 'ポテト', 'Price': 150})
    writer.writerow({'Menu': 'ドリンク', 'Price': 100})
```

```:出力結果（example.csv）
Menu,Price
ハンバーガー,300
ポテト,150
ドリンク,100

```

# CSVファイルを辞書形式で1行ずつ読み込む
```Py:DictReader()関数を使用する
# csvパッケージをimport
import csv

# CSVファイルを読みとりで開く
# ※１行ずつ空いてしまう場合、newline=''オプションをつける
with open('example.csv', 'r', newline='') as f:

    # csvパッケージのDictReader()関数を呼び出し、ファイル名を指定
    reader = csv.DictReader(f)

    # for文で辞書型で読み込んだファイルを1行ずつ変数に格納して
    # 変数['列名']と指定する
    for row in reader:
        print(row['Menu'], row['Price'])
```

```:出力結果（example.csv）
ハンバーガー 300
ポテト 150
ドリンク 100
```

# CSVファイルを読み込んでデータを更新する技
csv.DictWriterで読み込んだものをアップデートする方法が見つからなかったため、下記手順を行った。

1．辞書型変数準備
2．CSVfileをcsv.DictReaderで1行ずつ読み込み、1．で準備した辞書型変数に追加
3．辞書型変数の中の値を更新
4．更新し終えたら`with open('CSVファイル名', 'w', newline='')`でCSVファイルを上書きモードで開く
5．csv.DictWriter()関数を使ってCSVファイルを丸ごと上書きする

```:更新前のCSVファイル（example.csv）
Menu,Price
ハンバーガー,300
ポテト,150
ドリンク,100

```

```Py:例としてポテトを200円に更新する
# csvパッケージをimport
import csv

# 辞書型変数を準備
menu_dic = {}

# 列名設定
fieldnames = ['Menu', 'Price']

# CSVファイルを1行ずつ読み込んで、準備した辞書型変数に格納
with open('example.csv', 'r', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        menu_dic.update({row['Menu']: int(row['Price'])})

# 確認のため以下のコードを実行すると、下記のように出力される
# 
# 更新前辞書型変数
# {'ハンバーガー': 300, 'ポテト': 150, 'ドリンク': 100}

print('更新前辞書型変数')
print(menu_dic)

# 準備した辞書型変数内の「ポテト」の値段を更新
menu_dic['ポテト'] = int(200)

# 確認のため以下のコードを実行すると、下記のように出力される
# 
# 更新前辞書型変数
# {'ハンバーガー': 300, 'ポテト': 200, 'ドリンク': 100}
print('更新後辞書型変数')
print(menu_dic)

# CSVファイルを上書きモードで開く
with open('example.csv', 'w', newline='') as f:

    # csv.DictWriter()関数で列名までCSVファイルに出力
    writer = csv.DictWriter(f, fieldnames)
    writer.writeheader()

    # for文とitems()関数を使い、writerow関数を使って
    # 辞書型変数の中身を、辞書形式（{'列名': 値}）で
    # CSVファイルに出力する
    for menu, price in menu_dic.items():
        writer.writerow({'Menu': menu, 'Price': price})
```

```:更新後のCSVファイル（example.csv）
Menu,Price
ハンバーガー,300
ポテト,200
ドリンク,100

```

# CSVファイルの内容をデータの並び順ではない順番で表示後、データを追加・更新した上でCSVファイルに反映させる技

例として、値段の高いメニューから順番に表示して、メニューを追加・更新したものをCSVファイルに反映させるとする。

CSVファイルが下記のようになっている場合、一番高いダブルバーガーから表示して、最後はハンバーガを表示する。
その後、ポテト200円を追加して、ダブルバーガーを450円に変更するとする。

```:更新前CSVファイル（example.csv）
Menu,Price
ハンバーガー,300
チーズバーガー,400
ダブルバーガー,500
てりやきバーガー,350
```

手順は下記の通り。
1．CSVファイルを読み込んでOriginal_Menu_Dicに格納
2．Original_Menu_Dicをコピーして表示用の辞書Display_Menu_Dicに複製
3．一番値段の高いデータを表示して、そのデータをDisplay_Menu_Dicから削除、を繰り返す
4．全て表示してDisplay_Menu_Dicのデータがなくなったら、Original_Menu_Dicにポテト200円を追加して、ダブルバーガー450円を更新する
5．更新されたOriginal_Menu_DicをCSVファイルに上書き保存

```Py:
# deepcopy関数とcsvパッケージをimport
from copy import deepcopy
import csv


# 辞書型変数宣言
Original_Menu_Dic = {}
Display_Menu_Dic = {}

# 列名設定
fieldnames = ['Menu', 'Price']

# Original_Menu_Dic作成
with open('example.csv', 'r', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        Original_Menu_Dic.update({row['Menu']: int(row['Price'])})

# Display_Menu_Dic作成
Display_Menu_Dic = deepcopy(Original_Menu_Dic)

# 確認のため以下のコードを実行すると、下記のように出力される
#
# 更新前辞書型変数
# Original_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 500, 'てりやきバーガー': 350}
# Display_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 500, 'てりやきバーガー': 350}
print('更新前辞書型変数')
print('Original_Menu_Dic:', Original_Menu_Dic)
print('Display_Menu_Dic:', Display_Menu_Dic)
print('')

# メニューの中身を値段の高い順に表示
while len(Display_Menu_Dic) > 0:

    # 使用する変数を初期化
    # ※ループの度に初期化しないと前のループの段階で格納された値が残る
    tmp_menu = ''
    tmp_price = 0
    max_menu = ''
    max_price = 0

    # for文とitems()関数でメニューの中で一番高額なメニューを探しだす
    for tmp_menu, tmp_price in Display_Menu_Dic.items():

        # 一番高いメニューに何も入ってない（ループの一番最初）
        # あるいは
        # 読み込んだメニューの方が高額であれば
        # そのメニューを一番高額なメニューとして設定する
        if (max_menu == '') | (tmp_price > max_price):
            max_menu = tmp_menu
            max_price = tmp_price

    # 一番高額なメニューを表示して
    # 同じメニューを表示しないようにDisplay_Menu_Dicから削除する
    print('メニュー：', max_menu, '価格：', max_price)
    del Display_Menu_Dic[max_menu]

# 確認のため以下のコードを実行すると、下記のように出力される
# 表示用の辞書からはデータが全て消えているが
# 辞書データをdeepcopy()したため、元のメニューには影響がない
#
# メニュー表示後辞書型変数
# Original_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 500, 'てりやきバーガー': 350}
# Display_Menu_Dic: {}
print('')
print('メニュー表示後辞書型変数')
print('Original_Menu_Dic:', Original_Menu_Dic)
print('Display_Menu_Dic:', Display_Menu_Dic)
print('')

# ポテト200円を追加、ダブルバーガーの値段を450円に更新
Original_Menu_Dic['ポテト'] = int(200)
Original_Menu_Dic['ダブルバーガー'] = int(450)

# 確認のため以下のコードを実行すると、下記のように出力される
#
# メニュー更新後辞書型変数
# Original_Menu_Dic: {'ハンバーガー': 300, 'チーズバーガー': 400, 'ダブルバーガー': 450, 'てりやきバーガー': 350, 'ポテト': 200}
# Display_Menu_Dic: {}
print('メニュー更新後辞書型変数')
print('Original_Menu_Dic:', Original_Menu_Dic)
print('Display_Menu_Dic:', Display_Menu_Dic)
print('')

# 更新を終えたOriginal_Menu_Dicを
# csv.DictWriter()関数を使ってCSVファイルに上書きする
with open('example.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames)
    writer.writeheader()

    for menu, price in Original_Menu_Dic.items():
        writer.writerow({'Menu': menu, 'Price': price})
```

```:出力結果
メニュー： ダブルバーガー 価格： 500
メニュー： チーズバーガー 価格： 400
メニュー： てりやきバーガー 価格： 350
メニュー： ハンバーガー 価格： 300
```

```:更新後CSVファイル（example.csv）
Menu,Price
ハンバーガー,300
チーズバーガー,400
ダブルバーガー,450
てりやきバーガー,350
ポテト,200

```


[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)