プログラミング

Pythonの変数とコピー理解

バージョン3におけるPythonの「変数」「型」および「コピー」の取り扱い

Pythonはその簡潔さと高い可読性により、初心者から上級者まで幅広いユーザーに支持されているプログラミング言語です。特に、変数の取り扱いやデータのコピーに関しては、しばしば混乱を招くことがあります。この記事では、Python 3における「変数」「データ型」「コピー」の基本概念を深く掘り下げて、理解しやすく解説します。

1. Pythonにおける変数とオブジェクト

Pythonでは、変数は単なる「名前」として機能し、実際のデータ(オブジェクト)はメモリ上に格納されています。つまり、変数にデータを代入することは、そのデータのメモリ参照を変数に割り当てるということです。変数自体がデータを保持しているわけではなく、データが格納された場所(メモリのアドレス)を指し示しているのです。

たとえば、次のコードを考えてみましょう:

python
a = 10 b = a

この場合、abは両方とも整数の「10」を指し示しています。しかし、実際に変数が指しているのは同じ整数オブジェクトですが、これらは異なる変数であり、メモリ上でそれぞれの変数が同じオブジェクトを指しているに過ぎません。

2. ミュータブルとイミュータブル

Pythonでは、データ型は「ミュータブル(変更可能)」か「イミュータブル(変更不可)」かに分類できます。イミュータブルな型は、オブジェクトの状態を変更できません。例えば、inttuplestrなどがイミュータブルな型です。一方、リストや辞書、集合などのデータ型はミュータブルな型です。

イミュータブルな型の例:

python
x = 5 y = x x = 10 print(x) # 10 print(y) # 5

ここで、xの値を変更しても、yの値は変更されません。なぜなら、int型(整数)はイミュータブルなので、yxの参照を持っていたのではなく、xが指すオブジェクトをコピーしただけだからです。

ミュータブルな型の例:

python
lst1 = [1, 2, 3] lst2 = lst1 lst1.append(4) print(lst1) # [1, 2, 3, 4] print(lst2) # [1, 2, 3, 4]

この場合、lst2lst1を参照しているため、lst1に変更を加えると、lst2にもその変更が反映されます。リストはミュータブルな型であるため、変更が直接影響します。

3. 変数のコピーと参照

Pythonでは、変数をコピーする際にどのようにコピーされるかは非常に重要です。変数をコピーする方法には、浅いコピー(shallow copy)と深いコピー(deep copy)があります。

3.1 浅いコピー(Shallow Copy)

浅いコピーとは、オブジェクトの参照をコピーする方法です。新しいオブジェクトは作成されますが、その中身(子オブジェクト)は元のオブジェクトと同じ参照を持つことになります。

例えば、リストを浅くコピーする方法は以下のようになります:

python
import copy lst1 = [1, 2, 3, [4, 5]] lst2 = copy.copy(lst1) lst1[3].append(6) print(lst1) # [1, 2, 3, [4, 5, 6]] print(lst2) # [1, 2, 3, [4, 5, 6]]

ここで、lst1lst2は異なるリストオブジェクトですが、リスト内の子リスト([4, 5])は両方とも同じオブジェクトを参照しています。したがって、lst1の変更がlst2にも影響を与えます。

3.2 深いコピー(Deep Copy)

深いコピーとは、オブジェクトの全てのレベルを再帰的にコピーし、元のオブジェクトとは全く独立した新しいオブジェクトを作成する方法です。これにより、ネストされたオブジェクトも含めてすべての参照が新たに作成されるため、元のオブジェクトに対する変更はコピーに影響しません。

深いコピーを作成するには、copyモジュールのdeepcopy()関数を使用します:

python
import copy lst1 = [1, 2, 3, [4, 5]] lst2 = copy.deepcopy(lst1) lst1[3].append(6) print(lst1) # [1, 2, 3, [4, 5, 6]] print(lst2) # [1, 2, 3, [4, 5]]

この場合、lst2は完全に独立しており、lst1の変更はlst2には影響を与えません。

4. 変数の代入と関数引数

Pythonでは、関数に引数として渡す際に、実際に渡されるのは引数の参照(ポインタ)です。このため、引数がミュータブルな型であれば、関数内で変更を加えることができます。

python
def modify_list(lst): lst.append(4) my_list = [1, 2, 3] modify_list(my_list) print(my_list) # [1, 2, 3, 4]

上記のように、my_listは関数内で変更されますが、これはmy_listがリストというミュータブルな型であり、参照が渡されたからです。

イミュータブルな型の場合、関数内で変更を加えても元のオブジェクトは変わりません:

python
def modify_number(num): num = num + 5 my_num = 10 modify_number(my_num) print(my_num) # 10

ここでは、numが整数(イミュータブル)であるため、関数内で変更が加えられていても、元の変数my_numには影響を与えません。

5. 結論

Pythonにおける変数、データ型、およびコピーの理解は非常に重要です。特に、ミュータブル(変更可能)とイミュータブル(変更不可能)な型の違い、そして浅いコピーと深いコピーの違いをしっかりと把握しておくことは、バグの回避や効率的なコード作成に繋がります。Pythonは非常に直感的な言語ですが、データの取り扱いに関しては慎重を期す必要があり、実際にどのようにデータが管理されているのかを深く理解しておくことが、プログラミングのスキル向上に役立ちます。

Back to top button