質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

381閲覧

python 変数に辞書の値を入れたいが辞書に値がない時エラーになる

ngmg

総合スコア72

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

0クリップ

投稿2022/11/17 03:32

実現したいこと

辞書にあるアイテムを変数に入れたいのですが、何らかの理由で辞書の情報が欠けてしまっている場合があります。
ソースコードでは 'utao' のweightが欠けていますがageやheightが欠けている場合もあります。
辞書にアイテムが存在するということを前提に変数に入れているので欠けていた場合エラーが出てしまいます。

こういうイレギュラーを防ぐために辞書のチェックを行い、欠けていた場合は暫定の値を入れるみたいなことをしたいのですがどうすればよいでしょうか?

エラー

__sent_weight = list[name]['weight'] KeyError: 'weight'

該当のソースコード

list = { 'jone': {'age': 20, 'height': 180, 'weight': 75}, 'kaori': {'age': 18, 'height': 160, 'weight': 43}, 'utao': {'age': 16, 'height': 175}, } async def get_items(name, list): if list.get(name, None) is not None: _sent_age = list[name]['age'] _sent_height = list[name]['height'] _sent_weight = list[name]['weight']
kensoon👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meg_

2022/11/17 12:59

エラーと直接は関係ありませんが、変数名に「list」を使うのは止めましょう。
guest

回答2

0

get(key, default) を使いましょう。

python

1 _sent_weight = list[name].get('weight', -1)

投稿2022/11/18 06:45

int32_t

総合スコア20880

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

こんにちは。初学者の回答になりますので、話半分くらいで聞いていただけると幸いです。実際、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'])
16 175 0

ここでは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'])
16 175 None

ただ、この程度の働きであれば、関数を定義するまでもなく無名関数を使ってあげればよいでしょう。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'])
20 None

ただ、このコードにも少し問題がありまして、それはどんなキーでアクセスしてもエラーを吐かずNoneが返ってくることです。上の例だと誕生日だとかでアクセスをかけてもNoneが返ってきてしまいます。

python

1print(profiles['jone']['birthday'])
None

想定しない値へのアクセスは防ぎつつ、デフォルトの値を設定したいのであれば、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)
75 None

必ず与えられる属性は、"属性名: データ型"。与えられないかもしれない属性は"属性: 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}}

投稿2022/11/17 16:36

編集2022/11/17 17:09
kensoon

総合スコア48

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ngmg

2022/11/18 03:39

すごいです!とても丁寧に解説頂きありがとうございます! 自分の知らない方法を聞けて感激です。 こちらの方法でやってみたいと思います。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問