dict in dict な dict を dict にする


表題の件に付きまして、python初心者が実装しましたので報告いたします。
そもそも、dict in dict な dict とは、以下のようなdictのことです。

```json
{'name': '2M2VgARX', 'js': {'a': 0, 'b': 0, 'c': 'sdmo'}}
```

in しているdictのキーとin されているdictのキーは、合わせても一意なので、一つの辞書にまとめたいなーと思い、そんな関数を作ってみました。

変換後イメージ

```json
{'name': '2M2VgARX', 'js_a': 0, 'js_b': 0, 'js_c': 'sdmo'}
```


標準関数がありそうですが、自分の検索能力が低いため、
見つからなかったことはご了承下さい。

## 実装

```python
import secrets
import string


def dict_extract(item, prefix=''):
    return_dict = {}
    for key, value in item.items():
        if isinstance(value, dict):
            prefix += f'{key}_'
            return_dict.update(dict_extract(value, prefix))
        else:
            return_dict[prefix + key] = value
    return return_dict


def main():
    # create sample data
    items =[]
    alphabet = string.ascii_letters + string.digits
    password = ''.join(secrets.choice(alphabet) for i in range(8))
    for i in range(10):
        password = ''.join(secrets.choice(alphabet) for i in range(8))
        tmp = {'a': i, 'b': i*2, 'c': 'sdmo'}
        tmp_item = {'name': password, 'js': tmp}
        items.append(tmp_item)

    # print dict
    for item in items:
        print(dict_extract(item))


if __name__ == "__main__":
    main()
```

## 実行結果

```json
{'name': 'ZQ4NaBbc', 'js_a': 0, 'js_b': 0, 'js_c': 'sdmo'}
{'name': 'E5hS6IqS', 'js_a': 1, 'js_b': 2, 'js_c': 'sdmo'}
{'name': 'PC1xzQGi', 'js_a': 2, 'js_b': 4, 'js_c': 'sdmo'}
{'name': 'McJTGCjl', 'js_a': 3, 'js_b': 6, 'js_c': 'sdmo'}
{'name': '3hYsdwB8', 'js_a': 4, 'js_b': 8, 'js_c': 'sdmo'}
{'name': 'UKocBVhJ', 'js_a': 5, 'js_b': 10, 'js_c': 'sdmo'}
{'name': '3QVlhwOn', 'js_a': 6, 'js_b': 12, 'js_c': 'sdmo'}
{'name': 'bspVE4Hp', 'js_a': 7, 'js_b': 14, 'js_c': 'sdmo'}
{'name': 'rLLUxF4f', 'js_a': 8, 'js_b': 16, 'js_c': 'sdmo'}
{'name': '5wt3UCvR', 'js_a': 9, 'js_b': 18, 'js_c': 'sdmo'}
```

## 解説

- main()
  - \# create sample data  
    dict in dict を作ってるだけなので省略します。  
    secretsはrandomより安全みたいですよ。  
    公式→[https://docs.python.org/ja/3/library/secrets.html](https://docs.python.org/ja/3/library/secrets.html)
  - \# print dict  
    確認のための出力。
- dict_extract(item, prefix='')
  - 実装の本体です。
  - dict in dict in dict ...の事も考えて、再帰関数にしました。
  - valueの型がdictだったなら、再びvalueでdict_extractしてみる。
  - 最終的に返すdictのkeyが一意になるように、keyをアンダースコアでつないでみた。
  - 一意なことが確定しているのであれば、```prefix += f'{key}_'```部分をなくしていいかと。

## 余談

prefixをlistにしてjoinしたほうがスマートな気もします。
そのときは引数にlistを渡すのは避けましょう。
listはミュータブル、更新可能なオブジェクトです。
引数の初期値にミュータブルなオブジェクトを指定した場合、そのオブジェクトは関数定義時に生成されるので、該当の引数を省略して関数を呼び出すと同じオブジェクトが使われます。

コード

```python
def string_arg2(s_arg=['hatsumi']):
    s_arg.append(' san')
    print(''.join(s_arg))

for i in range(10):
    string_arg2()
```

結果

```code
hatsumi san
hatsumi san san
hatsumi san san san
hatsumi san san san san
hatsumi san san san san san
hatsumi san san san san san san
hatsumi san san san san san san san
hatsumi san san san san san san san san
hatsumi san san san san san san san san san
hatsumi san san san san san san san san san san
```

名刺管理サービスみたいな結果になりました。
ちなみにdictもミュータブルですのでお気をつけあそばせ。
初期化するときに一手間必要です。

```python
def string_arg2(s_arg=None):
    if s_arg is None:
        s_arg = ['hatsumi']
    s_arg.append(' san')
    print(''.join(s_arg))

for i in range(10):
    string_arg2()
```

## 募集

- 上記実装の標準関数はありそう。どう検索すれば見つかるか教えていただけると幸甚です。。
- dict in dict って一般的な呼び方あるんですかね。入れ子辞書？  
  略すとd in d になって、docker in dockerっぽくて紛らわしい・・