35
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

dictの比較クイズ

Last updated at Posted at 2021-06-06

dictの比較クイズ

はじめに

ほとんどのプログラミング言語ではキーと値を対応させたデータ構造を扱うことができます。
JavaではMap、JavaScriptではObject、Pythonではdictなどです。
今回はPythonのdictの比較の挙動が一部予想外だったのでクイズ形式で紹介します。

他の言語での挙動

JavaScriptのObjectの比較は非常にシンプルです。
参照が異なる場合は、たとえ中身が一緒であったとしてもfalseとなります。

const person1 = {name: "taro"};
const person2 = {name: "taro"};

console.log(person1 == person2);
> false

逆に参照が等しければ、代入後に値を書き換えてもtrueになります。

const person3 = person1;
person3.name = "jiro";

console.log(person1 == person3);
> true

それではPythonのdict比較クイズに挑戦してみましょう。

クイズ

第一問

中身が異なるdictを比較すると当然、Falseになります。

person1 = {"name": "taro"}
person2 = {"name": "taro", "age": 20}
person3 = {"name": "jiro"}

print(person1 == person2)
> False

print(person1 == person3)
> False

では値がすべて等しいdictを比較するとどうなるでしょうか?

person1 = {"name": "taro", "age": 20}
person2 = {"name": "taro", "age": 20}

print(person1 == person2)
> ???
第一問の正解はこちら

person1 = {"name": "taro", "age": 20}
person2 = {"name": "taro", "age": 20}

print(person1 == person2)
> True

予想外だったのではないでしょうか?
Pythonのdictの比較は、参照の比較ではなく、中の値まで比較します。

第二問

dictの値にさらにdictがある場合はどうなるでしょうか?あるいは値に配列がある場合はどうでしょうか?
ただし値に指定されているdictや配列の中身は等しいです。

person1 = {"name": "taro", child: {"name": "jiro"}}
person2 = {"name": "taro", child: {"name": "jiro"}}

print(person1 == person2)
> ???

person3 = {"name": "taro", children: [{"name": "jiro"}]}
person4 = {"name": "taro", children: [{"name": "jiro"}]}

print(person3 == person4)
> ???
第二問の正解はこちら

person1 = {"name": "taro", "child": {"name": "jiro"}}
person2 = {"name": "taro", "child": {"name": "jiro"}}

print(person1 == person2)
> True

person3 = {"name": "taro", "children": [{"name": "jiro"}]}
person4 = {"name": "taro", "children": [{"name": "jiro"}]}

print(person3 == person4)
> True

ともにTrueです。なんとなく傾向がつかめてきました。

第三問

ネストさせるのは第二問と同様ですが、person1person2childperson1を入れます。
この場合はどうなるでしょうか?

person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person1

print(person1 == person2)
> ???

ちなみにこのとき、person1person2childで無限に参照することができます。

person1["child"]
> {'name': 'taro', 'child': {...}}

person1["child"]["child"]
> {'name': 'taro', 'child': {...}}

person1["child"]["child"]["child"]
> {'name': 'taro', 'child': {...}}

これを踏まえて、答えがどうなるか予想してみてください。

第三問の正解はこちら

person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person1

print(person1 == person2)
> True

おそらく比較の実装がdictの中身の値のを比較する前に、参照が等しいかどうかを判定していると予想することができます。

第四問

第三問と似ていますが、person1childにはperson1person2childにはperson2をそれぞれ詰めます。
今回、この記事を投稿しようと思ったのはこの第四問の挙動が面白かったからなので、ぜひ予測してみてください。

person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person2

print(person1 == person2)
> ???
第四問の正解はこちら

person1 = {"name": "taro"}
person1["child"] = person1

person2 = {"name": "taro"}
person2["child"] = person2

print(person1 == person2)
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> RuntimeError: maximum recursion depth exceeded in cmp

エラーになりました。再帰呼び出しが上限に到達したというエラーです。
参照が異なるとネストを無限にたどってしまうのですね。

さいごに

どのくらい正解したでしょうか?予想外な挙動があって勉強になった、面白かったって方はぜひLGTMしてね♥

35
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?