sec02 - listのコピー(2) [type() と id()]

このレクチャーでは、前回の『[コラム] Pythonの変数とデータの管理方法』で学習した内容を簡潔に復習しつつ、type()id()関数の使い方を学んでいきます。

スポンサーリンク

数値や文字列のtype(), id()を確認する

数値のtype(), id()を確認する

それでは、数値データのtypeとidを確認しながら、Pythonがどのようにデータを管理しているのかを確認しましょう。

データ型を調べるにはtype()関数を使い、idを調べるにはid()関数を使います。

num1 = 5
num2 = num1
num3 = 5
num4 = 5.0

print('num1:', num1, type(num1), id(num1))  # 5 <class 'int'> 140715724784680
print('num2:', num2, type(num2), id(num2))  # 5 <class 'int'> 140715724784680
print('num3:', num3, type(num3), id(num3))  # 5 <class 'int'> 140715724784680
print('num4:', num4, type(num4), id(num4))  # 5.0 <class 'float'> 1872433047024

上記コードを実行すると、下図のような出力結果を得られます。(※ただし、id()の値は、実行するたび変わります。)

int5が代入された各変数が参照しているid番号は全て同じであることが確認でき、int5とfloat5.0は別のデータとして生成され、id番号も変わっていることが確認できます。

文字列のtype(), id()を確認する

文字列の場合のデータの管理方法は、基本的には数値と同じですが、文字の生成方法の違いにより、別のデータとして作成されることがあります。

str1 = 'Hello'
str2 = 'Hello'
str3 = '{}llo'.format('He')
str4 = 'Hello World'[:5]

print('str1:', str1, type(str1), id(str1))  # Hello <class 'str'> 2321373022176
print('str2:', str2, type(str2), id(str2))  # Hello <class 'str'> 2321373022176
print('str3:', str3, type(str3), id(str3))  # Hello <class 'str'> 2321372756752
print('str4:', str4, type(str4), id(str4))  # Hello <class 'str'> 2321373022656

上記コードを実行すると、変数"str1"と"str2"は同じid番号になりますが、"str3"と"str4"は違う番号になります。全ての変数は'Hello'という文字列が代入されているのですが、format()メソッドやスライスを使うと、文字の生成方法の違いによって異なるデータとして管理されることが確認できます。

listのtype(), id()を確認する

次に、listとその要素の情報を確認しましょう。(下記コードは、"original_list"を"original"と略して記述しています。)

original = [1, 2, 3]

print('original:', original, type(original), id(original))  # [1, 2, 3] <class 'list'> 2206846990848
print('original[0]:', original[0], type(original[0]), id(original[0]))  # 1 <class 'int'> 140715703092136
print('original[1]:', original[1], type(original[1]), id(original[1]))  # 2 <class 'int'> 140715703092168
print('original[2]:', original[2], type(original[2]), id(original[2]))  # 3 <class 'int'> 140715703092200

コードを実行すると、上記のコメントで説明されているとおり、listやintの情報を得られます。

print(original)を実行すると、[1, 2, 3]と出力されるため、数値も内包しているように見えるかもしれません。しかし、listはintの値を内包しているのではなく、それぞれの値のデータを参照していることになります。type()id()を使って情報を確認することにより、Pythonのデータ管理に関する理解が深まります。

(※プログラミングの開発中は、listの要素情報を簡単に確認する必要があるため、[1, 2, 3]のように出力されることは悪いことではありません。しかし、listなどの構造体をコピーする場面で問題が起こることがあるため、情報の確認方法を知っておく必要はあります。)

スポンサーリンク

コピーしたlistのtype(), id()を確認する

同じlistオブジェクトを参照する例

copied = originalでコピーしたlistのid番号を確認してみましょう。

original = [1, 2, 3]
copied = original

print('original:', original, type(original), id(original))  # [1, 2, 3] <class 'list'> 1896325197824
print('copied:', copied, type(copied), id(copied))          # [1, 2, 3] <class 'list'> 1896325197824

上記コードを実行すると、両方のlistが同じid番号になっていることが確認できます。

異なるlistオブジェクトを参照する例

次のコードのようにlistのデータを生成することで、それぞれの変数は異なるlistを参照することが出来ます。

original = [1, 2, 3]
copied = [1, 2, 3]
print('original:', original, type(original), id(original))  # [1, 2, 3] <class 'list'> 2916726571520
print('copied:', copied, type(copied), id(copied))          # [1, 2, 3] <class 'list'> 2916726483584
original = [1, 2, 3]
copied = original[:]
print('original:', original, type(original), id(original))  # [1, 2, 3] <class 'list'> 1795666379264
print('copied:', copied, type(copied), id(copied))          # [1, 2, 3] <class 'list'> 1795666291328
original = [1, 2, 3]
copied = original.copy()
print('original:', original, type(original), id(original))  # [1, 2, 3] <class 'list'> 2438443153920
print('copied:', copied, type(copied), id(copied))          # [1, 2, 3] <class 'list'> 2438443176704

ネストされたlistのtype(), id()を確認する (Shallow copy)

ネストされたlistの、"外側"と"内側"のlistのidを確認してみます。

original = [[1, 2]]
copied = original.copy()
copied[0][0] = 99

print('original:', original, type(original), id(original))  # [[99, 2]] <class 'list'> 3051058725056
print('copied:', copied, type(copied), id(copied))          # [[99, 2]] <class 'list'> 3051058637248
print('original[0]:', original[0], type(original[0]), id(original[0]))  # [99, 2] <class 'list'> 3051058739776
print('copied[0]:', copied[0], type(copied[0]), id(copied[0]))          # [99, 2] <class 'list'> 3051058739776

"外側"のlistについては、copy()メソッドによって複製されたので、違うidになっています。しかし、"内側"のlistについては、同じidになっていることが分かります。そして、"内側"のlistの要素に対して編集が行われると、他方のlistにまで影響が及びます。

つまり、Shallow copy (シャローコピー)では、ネストされたlistの全てを、異なるlistとして扱えないことになります。(※ただし、ネストされたlistの要素に対して編集を行うことが無いのであれば、Shallow copyで十分です。)

Deep copyした後のtype(), id()を確認する

次に、Deep copy(ディープコピー)した後のtype(), id()を確認します。Pythonでは、copyモジュールのdeepcopy()関数を使用します。

import copy

original = [[1, 2]]
copied = copy.deepcopy(original)
copied[0][0] = 99

print('original:', original, type(original), id(original))  # [[1, 2]] <class 'list'> 1461724428864
print('copied:', copied, type(copied), id(copied))          # [[99, 2]] <class 'list'> 1461724451776
print('original[0]:', original[0], type(original[0]), id(original[0]))  # [1, 2] <class 'list'> 1461724327744
print('copied[0]:', copied[0], type(copied[0]), id(copied[0]))          # [99, 2] <class 'list'> 1461724332416

ネストされたlistも含め、全てのidが異なっていることが確認できます。これで、ネストされたlistに対して編集を行っても、他方に影響を与えることは無くなります。

(※ ただし、全てを複製することによって、メモリ使用量や処理速度に影響が出ることがありますので、処理の内容によってShallow copyとDeep copyを使い分けましょう。)

スポンサーリンク