Pythonのディープコピーは完璧ではありません

Pythonの浅いコピーと深いコピーの違いについてはすでにご存知だと思います。浅いコピーは元のオブジェクトのメモリスペースを再適用することですが、元のオブジェクトのサブオブジェクトが可変オブジェクトである場合でも、参照関係があります。深いコピーまた、メモリスペースを再申請し、新しい子オブジェクトを再帰的に作成して新しいオブジェクトにコピーします。元のオブジェクトとその子オブジェクトは互いに独立しており、新しいオブジェクトは元のオブジェクトとは関係ありません。

ただし、ディープコピーは完全ではありません。最初にコードを確認してください。最初にプログラムの出力を予測してから実行し、期待値が一貫しているかどうかを確認できます。

import copy
x =[1]
x.append(x)
y = copy.deepcopy(x)

# 次のコマンドの出力は何ですか?
x == y

プログラムが3行目まで実行されると、xはすでに無限にネストされたリストですが、4行目まで実行されると、プログラムは深くコピーされ、新しいサブオブジェクトが再帰的に作成されますが、メモリオーバーフローは発生しません。間違った、これはなぜですか?

実際、これは、ディープコピー機能deepcopyが、コピーされたオブジェクトとそのIDを記録するための辞書を保持しているためです。コピーの過程で、コピーするオブジェクトがすでに辞書に保存されている場合は、辞書から直接返されます。対応するソースコードを理解できます。

def deepcopy(x, memo=None, _nil=[]):"""Deep copy operation on arbitrary Python objects.
      
 See the module's __doc__ string for more info."""
  
 if memo is None:
  memo ={}
 d =id(x) #コピーされたオブジェクトxのIDを照会します
 y = memo.get(d, _nil) #オブジェクトがすでに辞書に保存されているかどうかを照会します
 if y is not _nil:return y #コピーするオブジェクトがすでに辞書に保存されている場合は、直接返します
        ...

プログラムが7行目まで実行されて2つのオブジェクトの値を比較すると、エラーが報告されます。理由は何ですか。

xは無限にネストされたリストであり、yはxから深くコピーされるため、理論的にはx == yはTrueであるはずですが、比較演算子==の場合、==演算子はオブジェクトを再帰的にトラバースします。すべての値を比較し、1つずつ比較します。スタックがクラッシュするのを防ぐために、Pythonは再帰レベルの数を制限する必要があり、それが際限なく続くことはないため、制限に達すると、Pythonインタープリターはエラーからジャンプします。

>>> import copy
>>> x=[1]>>> x.append(x)>>> x
[1,[...]]>>> y = copy.deepcopy(x)>>> x == y
Traceback(most recent call last):
 File "<stdin>", line 1,in<module>
RecursionError: maximum recursion depth exceeded in comparison
>>>

その理由は、Pythonの再帰レベルの数が制限されていることでもあります。sysモジュールには、再帰レベルの数を取得するメソッドがあります。

>>> import sys
>>> sys.getrecursionlimit()1000

もちろん、再帰レベルの数をリセットすることもできます。

>>> import sys
>>> sys.getrecursionlimit()1000>>> sys.setrecursionlimit(10000)>>> sys.getrecursionlimit()10000

無限大を設定することは可能ですか?理論的には可能ですが、プログラムのクラッシュも確実です。自分でテストを行うことができます。

要約すると、ディープコピーの欠点は、オブジェクト内にそれ自体への参照がある場合、無限ループが発生しやすいことですが、参照とシャローコピーは次のようになりません。

>>> import copy
>>> x=[1]>>> x.append(x)>>> x_alias = x
>>> x_copy = copy.copy(x)>>> x_deepcopy = copy.deepcopy(x)>>> x == x_alias
True
>>> x == x_copy
True
>>> x == x_deepcopy
Traceback(most recent call last):
 File "<stdin>", line 1,in<module>
RecursionError: maximum recursion depth exceeded in comparison
>>>

Recommended Posts

Pythonのディープコピーは完璧ではありません
Python 3.9が登場!
なぜpythonが人気なのか
Pythonは短いクロール音楽です
Pythonはゆっくりと衰退しています
pythonは解釈された言語ですか?
pythonは解釈された言語ですか
Pythonは短世界の流行マップです
Pythonマルチスレッドの深い理解
Pythonは短い_SVMテストです
pythonコードは大文字と小文字を区別しますか
pythonのイントロスペクションとは何ですか
pythonのオブジェクト指向とは何ですか
Python変数スコープとは