Python エラーの修正 - Dictionary Changed Size During Iteration

Isaac Tony 2023年1月30日
  1. 辞書の浅いコピーを作成する
  2. 辞書アイテムをリストにキャストする
  3. 空のリストにキーを追加する
Python エラーの修正 - Dictionary Changed Size During Iteration

このランタイムエラーは、反復中にディクショナリオブジェクトの新しいエントリを削除、変更、または追加したときに発生します。このエラーは、辞書を反復処理するときに発生しますが、使用するプログラミング言語に関係なく、事実上すべての反復可能オブジェクトです。

以下のコードスニペットは、辞書を反復処理すると同時に変更を加えるときにこのエラーがどのように発生するかを示しています。

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021}

for x in cars.keys():
    cars["color"] = "white"
print(x)

上記のコードブロックでは、反復中に元の辞書に新しいアイテムを追加します。これにより、ランタイムエラーが返され、反復中にディクショナリのサイズが変更されたことが通知されます。これは、同時に反復している間はディクショナリを変更できないことを意味します。

サンプルコード:

Traceback (most recent call last):
File "<string>", line 8, in <module>
RuntimeError: dictionary changed size during iteration

オブジェクトに対して反復を実行している間、削除、追加、または変更の両方が変更と見なされ、反復中に実行することはできません。以下のコード例は、反復中に辞書を変更した場合にもこのエラーが続くことを示しています。したがって、反復中に既存のアイテムを辞書から削除しても、同じエラーが発生します。

サンプルコード:

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021}

for x in cars.keys():
    del cars["model"]
print(cars)

出力:

Traceback (most recent call last):
File "<string>", line 8, in <module>
RuntimeError: dictionary changed size during iteration

Python 3 では、変化するオブジェクトを反復処理することは、コードを書くための悪いスタイルであり、安全ではないと見なされます。一般に、プログラミングでは、オブジェクトを同時に反復しながら変更することはできません。このルールは、リストや配列などの反復可能オブジェクトにも適用されます。

ただし、関数がオブジェクトを変更する場合は、関数が元のオブジェクトのコピーのみを変更し、元のオブジェクトはそのままにしておく必要があります。これは、オブジェクトを同時に反復しながらオブジェクトに変更を加えるために広く使用されているアプローチの 1つです。

これは、最終的にメモリの枯渇につながる可能性のある無限ループのインスタンスを回避するための優れた方法であり、最良の方法です。このエラーを処理するためにいくつかの解決策を使用できます。ここでそれぞれについて説明します。

辞書の浅いコピーを作成する

Python には、元のオブジェクトにバインドせずにオブジェクトのコピーを作成できる copy() モジュールが用意されています。これにより、元のオブジェクトをそのままにして、オブジェクトのコピーを自由に変更できます。

Python で代入演算子を使用しても同じことは実現できないことに注意してください。代入演算子を使用すると、元のオブジェクトのコピーは作成されませんが、元のオブジェクトを参照する変数が作成されます。

したがって、新しいオブジェクトに加えられた変更は、元のオブジェクトにも影響します。新しい開発者は、この演算子を誤用することがよくあります。

サンプルコード:

import copy

cars = {
    "brand": "Tesla",
    "model": "Model S Plaid",
    "year": 2021,
}

# creating a shallow copy
cars_copy = copy.copy(cars)

for x in cars_copy.keys():
    cars["color"] = "black"

print(cars)
print(cars_copy)

出力:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021}

提供されているサンプルコードでは、コピーモジュールの copy 関数を使用して、元の辞書に影響を与えることなく自由に反復できる辞書コピーを作成しました。辞書のコピーに変更を加えると、エラーが発生することなく辞書を反復処理できます。

または、以下に示すように、2つのアスタリスク演算子と呼ばれることが多い**演算子を使用して、上記のコードを書き直すこともできます。

サンプルコード:

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021}

# creating a shallow copy
cars_copy = {**cars}


for x in cars_copy.keys():
    cars["color"] = "black"

print(cars)

**演算子は、あるディクショナリからキーと値のペアを取得し、それらを別のディクショナリにダンプできます。

演算子は Python でキーワード引数を渡すために広く使用されていますが、上記のコードで辞書を解凍してキーと値のペアを取得するために演算子を使用しました。次に、ディクショナリのコピーを作成し、解凍された値をこの新しいディクショナリにダンプします。

出力:

'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

ディクショナリからキーと値のペアを削除することも例外ではなく、反復を実行する場合も同様のアプローチに従う必要があります。したがって、同じ手順を使用して、以下に示すように、model という名前のキーとその値 Model S Plaid を削除します。

サンプルコード:

import copy

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021, "color": "black"}

cars_copy = copy.copy(cars)

for x in cars_copy.keys():
    if x == "model":
        del cars["model"]

print(cars)

出力:

{'brand': 'Tesla', 'year': 2021, 'color': 'black'}

もう 1つの解決策は、キーのコピーを作成し、それを辞書の変更中に繰り返すことができるようにすることです。ただし、これは Python 2 でのみ機能し、Python 3 では機能しません。これは、Python 3 で実行すると、キーが反復可能オブジェクトを返さないためです。

サンプルコード:

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021, "color": "black"}

key_copys = list(cars.keys())
print(key_copys)

for key in list(key_copys):
    if cars[key] == "model":
        cars.pop("model")

print(cars)

サンプル出力:

['brand', 'model', 'year', 'color']
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

辞書アイテムをリストにキャストする

変更を加えている間はディクショナリを反復処理できないため、代わりにキャストリストを作成し、ディクショナリを変更しながらリストを反復処理することができます。元のディクショナリの代わりにキャストリストを繰り返し処理しても、ランタイムエラーは返されません。

サンプルコード:

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021}

for i in list(cars):
    cars["color"] = "black"

print(cars)

出力:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

空のリストにキーを追加する

反復中に辞書を変更しないようにするために、反復の実行中に辞書キーを含む空のリストを作成できます。この空のリストを使用して、削除または変更するすべてのキーを追加してから、pop() 関数を使用してキーを削除するか、append 関数を使用して新しいキーと値のペアを追加できます。

これは、以下のコードに示すように実行できます。

サンプルコード:

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021}

list = []

for i in cars:
    list.append(i)

for x in list:
    if x == "model":
        cars.pop(x)

print(cars)

出力:

{'brand': 'Tesla', 'year': 2021}

以下に示すように、for ループを使用して反復しながら、同じ手順を使用して新しいキーと値のペアをディクショナリに追加できます。

サンプルコード:

cars = {"brand": "Tesla", "model": "Model S Plaid", "year": 2021}

list = []

for i in cars:
    list.append(i)

for x in list:
    cars["color"] = "black"

print(cars)

出力:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
著者: Isaac Tony
Isaac Tony avatar Isaac Tony avatar

Isaac Tony is a professional software developer and technical writer fascinated by Tech and productivity. He helps large technical organizations communicate their message clearly through writing.

LinkedIn

関連記事 - Python Dictionary