こんにちは。初学者の回答になりますので、話半分くらいで聞いていただけると幸いです。実際、asyncのところの表記は今ひとつ理解ができていませんので、「欠けていた場合は暫定の値を入れる」というところに絞って回答させていただきます。
さて本題ですが、コードと質問の方を読ませていただく限り、defaultdictで解決できると思われます。defaultdictはコンテナデータ型の一つで、生成時に関数を引数として与えることで、その関数が自動的に初期化を行ってくれるようになります。サンプルコードとしてはこんな感じ。
python
1from collections import defaultdict
2
3utao_profile = defaultdict(int)
4utao_profile['age'] = 16
5utao_profile['height'] = 175
6
7print(utao_profile['age'])
8print(utao_profile['height'])
9print(utao_profile['weight'])
ここではintを関数として与えています。intは引数を取らない時に0を返しますので、存在しない'weight'というキーを読み込んだときにもエラーを吐かず、ゼロを出力してくれます。
もちろんこの関数は自作しても良いです。値が存在しないということで、Noneを格納して欲しければ、Noneを返す関数を自作して与えてあげればOK。
python
1from collections import defaultdict
2
3def NoneMaker():
4 return None
5
6utao_profile = defaultdict(NoneMaker)
7utao_profile['age'] = 16
8utao_profile['height'] = 175
9
10print(utao_profile['age'])
11print(utao_profile['height'])
12print(utao_profile['weight'])
ただ、この程度の働きであれば、関数を定義するまでもなく無名関数を使ってあげればよいでしょう。lambda: 値とすることで、任意の値を返す名無しの関数が作れます。
ということで、書かれているものと(おおむね)同じ働きをするコードは以下のようになります。
python
1from collections import defaultdict
2
3
4jone_profile = defaultdict(lambda: None)
5jone_profile['age'] = 20
6jone_profile['height'] = 180
7jone_profile['weight'] = 75
8
9kaori_profile = defaultdict(lambda: None)
10kaori_profile['age'] = 18
11kaori_profile['height'] = 160
12kaori_profile['weight'] = 43
13
14utao_profile = defaultdict(lambda: None)
15utao_profile['age'] = 16
16utao_profile['height'] = 175
17
18profiles = {
19 'jone': jone_profile,
20 'kaori': kaori_profile,
21 'utao': utao_profile,
22}
23
24print(profiles['jone']['age'])
25print(profiles['utao']['weight'])
ただ、このコードにも少し問題がありまして、それはどんなキーでアクセスしてもエラーを吐かずNoneが返ってくることです。上の例だと誕生日だとかでアクセスをかけてもNoneが返ってきてしまいます。
python
1print(profiles['jone']['birthday'])
想定しない値へのアクセスは防ぎつつ、デフォルトの値を設定したいのであれば、NamedTupleとOptionalの併用も視野に入るでしょう。NamedTupleは.属性名でアクセスできるコンテナの一種で、Optionalと併用することで、「基本はNoneで、値が入力された場合はそちらを採用」といった形で利用することが可能です。この方法であれば存在していない属性へのアクセスが防げて、かつ決まったデータ型を格納することもできるようになります。
python
1from typing import NamedTuple, Optional
2
3class Profile(NamedTuple):
4 name: str
5 age: Optional[int] = None
6 height: Optional[int] = None
7 weight: Optional[int] = None
8
9jone_profile = Profile(name='jone', age=20, height=180, weight=75)
10utao_profile = Profile(name='utao', age=16, height =175)
11
12print(jone_profile.weight)
13print(utao_profile.weight)
必ず与えられる属性は、"属性名: データ型"。与えられないかもしれない属性は"属性: Optional[想定されるデータ型] = デフォルト値"という使い方です。こいつなら、
python
1print(utao_profile.birthday)
というアクセスがあっても、
AttributeError: 'Profile' object has no attribute 'birthday'
「そんなもん設定されてないよ」と返してくれます。
以上、なにかの参考になれば幸いです。
(追記)
だーっと書いて見直してから気づいたのですが、「辞書側には干渉できない」という可能性が頭から抜けてました。その場合は力技にはなりますが、for文+valuesで中身の辞書を取り出してから、setdefaultメソッドで片っ端から値を代入するのが手っ取り早いかもしれません。setdefaultは辞書に備え付けられたメソッドの一つで、setdefault(key, value)とすることで、keyが存在しない場合のみ、valueが新たな値として追加されるという働きをします。
python
1profiles = {
2 'jone': {'age': 20, 'height': 180, 'weight': 75},
3 'kaori': {'age': 18, 'height': 160, 'weight': 43},
4 'utao': {'age': 16, 'height': 175},
5}
6
7for profile_dict in profiles.values():
8 profile_dict.setdefault('age', None)
9 profile_dict.setdefault('height', None)
10 profile_dict.setdefault('weight', None)
11
12print(profiles)
{'jone': {'age': 20, 'height': 180, 'weight': 75}, 'kaori': {'age': 18, 'height': 160, 'weight': 43}, 'utao': {'age': 16, 'height': 175, 'weight': None}}