コピー

Pythonでは、リストのようなオブジェクトは代入では参照になる。


a = [ 1, 2 ]
b = a
b[1] = 3
print a # [1, 3]

copyを使うと成分をコピーする。


import copy

a = [ 1, 2 ]
b = copy.copy(a)
b[1] = 3
print b # [1, 3]
print a # [1, 2]

ただし、深いコピー(ディープコピー)はしない。


import copy

a = [ [1, 2], [3, 4] ]
b = copy.copy(a)
b[1][1] = 3
print b # [ [1, 2], [3, 3] ]
print a # [ [1, 2], [3, 3] ]

ディープコピーは次のようにする。


import copy

a = [ [1, 2], [3, 4] ]
b = copy.deepcopy(a)
b[1][1] = 3
print b # [ [1, 2], [3, 3] ]
print a # [ [1, 2], [3, 4] ]

ディープコピーはどこまでも深くコピーするので、注意が必要である。適当な深さまでコピーしたい場合は、クラスなら__copy__を定義すればよい。
例えば、2次元の点と線を次のように定義する。


class cEdge:
def __init__(self, vs, ve):
self.v = [ vs, ve ]

def __str__(self):
return str(self.v[0]) + " - " + str(self.v[1])

class cVertex:
def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"

これをコピーして、点を変えると、


v1 = cVertex(1, 2)
v2 = cVertex(2, 3)
v3 = cVertex(3, 4)
e1 = cEdge(v1, v2)
e2 = copy.copy(e1)
e2.v[1] = v3
print e1 # (1, 2) - (3, 4)

変えていないつもりの辺まで変わってしまう。
それではということでディープコピーを使って、点の座標を変えると、


v1 = cVertex(1, 2)
v2 = cVertex(2, 3)
e1 = cEdge(v1, v2)
e2 = copy.deepcopy(e1)
e2.v[1].x = 3
print e1 # (1, 2) - (2, 3)

今度は点を動かしたつもりなのに動いてくれない。
このようなときは、クラスに__copy__メソッドを定義するとよい。


import copy

class cEdge:
def __init__(self, vs, ve):
self.v = [ vs, ve ]

def __str__(self):
return str(self.v[0]) + " - " + str(self.v[1])

def __copy__(self):
e = cEdge(self.v[0], self.v[1])
return e

class cVertex:
def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"

v1 = cVertex(1, 2)
v2 = cVertex(2, 3)
v3 = cVertex(3, 4)
e1 = cEdge(v1, v2)
e2 = copy.copy(e1)
e2.v[1].x = 3
print e1 # (1, 2) - (3, 3)
e2.v[1] = v3
print e1 # (1, 2) - (3, 3)