表題の件に付きまして、python初心者が実装しましたので報告いたします。
そもそも、dict in dict な dict とは、以下のようなdictのことです。
{'name': '2M2VgARX', 'js': {'a': 0, 'b': 0, 'c': 'sdmo'}}
in しているdictのキーとin されているdictのキーは、合わせても一意なので、一つの辞書にまとめたいなーと思い、そんな関数を作ってみました。
変換後イメージ
{'name': '2M2VgARX', 'js_a': 0, 'js_b': 0, 'js_c': 'sdmo'}
標準関数がありそうですが、自分の検索能力が低いため、
見つからなかったことはご了承下さい。
実装
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()
実行結果
{'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 - # print dict
確認のための出力。
- # create sample data
- 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はミュータブル、更新可能なオブジェクトです。
引数の初期値にミュータブルなオブジェクトを指定した場合、そのオブジェクトは関数定義時に生成されるので、該当の引数を省略して関数を呼び出すと同じオブジェクトが使われます。
コード
def string_arg2(s_arg=['hatsumi']):
s_arg.append(' san')
print(''.join(s_arg))
for i in range(10):
string_arg2()
結果
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もミュータブルですのでお気をつけあそばせ。
初期化するときに一手間必要です。
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っぽくて紛らわしい・・