ฉันต้องการสร้างสำเนาของวัตถุ ฉันต้องการให้วัตถุใหม่มีคุณสมบัติทั้งหมดของวัตถุเก่า (ค่าของเขตข้อมูล) แต่ฉันต้องการมีวัตถุอิสระ ดังนั้นถ้าฉันเปลี่ยนค่าของเขตข้อมูลของวัตถุใหม่วัตถุเก่าไม่ควรได้รับผลกระทบจากสิ่งนั้น
ฉันต้องการสร้างสำเนาของวัตถุ ฉันต้องการให้วัตถุใหม่มีคุณสมบัติทั้งหมดของวัตถุเก่า (ค่าของเขตข้อมูล) แต่ฉันต้องการมีวัตถุอิสระ ดังนั้นถ้าฉันเปลี่ยนค่าของเขตข้อมูลของวัตถุใหม่วัตถุเก่าไม่ควรได้รับผลกระทบจากสิ่งนั้น
คำตอบ:
เพื่อให้ได้สำเนาที่เป็นอิสระจากวัตถุคุณสามารถใช้copy.deepcopy()
ฟังก์ชันได้
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการตื้นและลึกคัดลอกโปรดดูคำตอบอื่น ๆ คำถามนี้และคำอธิบายที่มีความสุขในคำตอบนี้ไปคำถามที่เกี่ยวข้อง
ฉันจะสร้างสำเนาของวัตถุใน Python ได้อย่างไร
ดังนั้นถ้าฉันเปลี่ยนค่าของเขตข้อมูลของวัตถุใหม่วัตถุเก่าไม่ควรได้รับผลกระทบจากสิ่งนั้น
คุณหมายถึงวัตถุที่ไม่แน่นอน
ใน Python 3 ลิสต์รับcopy
เมธอด (ใน 2 คุณจะใช้สไลซ์เพื่อทำสำเนา):
>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True
สำเนาตื้นเป็นเพียงสำเนาของภาชนะนอกสุด
list.copy
เป็นสำเนาตื้น:
>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
คุณไม่ได้รับสำเนาของวัตถุภายใน มันเป็นวัตถุเดียวกัน - ดังนั้นเมื่อพวกมันกลายพันธุ์การเปลี่ยนแปลงจะปรากฏขึ้นในภาชนะทั้งสอง
สำเนาลึกเป็นสำเนาซ้ำของวัตถุภายในแต่ละชิ้น
>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
การเปลี่ยนแปลงจะไม่สะท้อนให้เห็นในต้นฉบับเฉพาะในการคัดลอก
วัตถุที่ไม่เปลี่ยนรูปมักไม่จำเป็นต้องทำการคัดลอก ในความเป็นจริงถ้าคุณพยายาม Python จะให้วัตถุต้นฉบับแก่คุณ:
>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'
Tuples ไม่มีวิธีการคัดลอกดังนั้นลองมาลองกับชิ้น:
>>> tuple_copy_attempt = a_tuple[:]
แต่เราเห็นว่ามันเป็นวัตถุเดียวกัน:
>>> tuple_copy_attempt is a_tuple
True
ในทำนองเดียวกันสำหรับสตริง:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
และสำหรับ frozensets แม้ว่าพวกเขาจะมีcopy
วิธี:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
ควรคัดลอกวัตถุที่ไม่เปลี่ยนรูปถ้าคุณต้องการคัดลอกวัตถุภายในที่ไม่แน่นอน
>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)
อย่างที่เราเห็นเมื่อวัตถุภายในของสำเนาถูกทำสำเนาต้นฉบับจะไม่เปลี่ยนแปลง
วัตถุที่กำหนดเองมักจะเก็บข้อมูลไว้ใน__dict__
แอตทริบิวต์หรือใน__slots__
(โครงสร้างหน่วยความจำคล้าย tuple)
ในการสร้างวัตถุที่__copy__
คัดลอกได้ให้กำหนด(สำหรับสำเนาตื้น) และ / หรือ__deepcopy__
(สำหรับสำเนาลึก)
from copy import copy, deepcopy
class Copyable:
__slots__ = 'a', '__dict__'
def __init__(self, a, b):
self.a, self.b = a, b
def __copy__(self):
return type(self)(self.a, self.b)
def __deepcopy__(self, memo): # memo is a dict of id's to copies
id_self = id(self) # memoization avoids unnecesary recursion
_copy = memo.get(id_self)
if _copy is None:
_copy = type(self)(
deepcopy(self.a, memo),
deepcopy(self.b, memo))
memo[id_self] = _copy
return _copy
โปรดทราบว่าdeepcopy
ช่วยให้พจนานุกรมบันทึกความทรงจำของid(original)
(หรือหมายเลขประจำตัว) เพื่อคัดลอก เมื่อต้องการเพลิดเพลินกับพฤติกรรมที่ดีกับโครงสร้างข้อมูลแบบเรียกซ้ำตรวจสอบให้แน่ใจว่าคุณยังไม่ได้คัดลอกและถ้าคุณมีให้ส่งคืน
ดังนั้นทำวัตถุ:
>>> c1 = Copyable(1, [2])
และcopy
ทำสำเนาตื้น ๆ :
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
และdeepcopy
ตอนนี้ทำสำเนาลึก:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]
คัดลอกตื้นด้วย copy.copy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]
# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]
คัดลอกลึกกับ copy.deepcopy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]
เอกสารประกอบ: https://docs.python.org/3/library/copy.html
ทดสอบกับ Python 3.6.5
ฉันเชื่อว่าสิ่งต่อไปนี้ควรใช้กับคลาสที่มีความประพฤติดีใน Python:
def copy(obj):
return type(obj)(obj)
(แน่นอนฉันไม่ได้พูดถึงที่นี่เกี่ยวกับ "สำเนาลึก" ซึ่งเป็นเรื่องที่แตกต่างกันและซึ่งอาจไม่เป็นแนวคิดที่ชัดเจนมาก - ลึกพอลึกแค่ไหน?)
จากการทดสอบของฉันกับ Python 3 สำหรับวัตถุที่ไม่เปลี่ยนรูปแบบเช่น tuples หรือสตริงมันจะส่งคืนวัตถุเดียวกัน (เพราะไม่จำเป็นต้องทำสำเนาตื้นของวัตถุที่เปลี่ยนรูปได้) แต่สำหรับรายการหรือพจนานุกรมจะสร้างสำเนาตื้นที่เป็นอิสระ .
แน่นอนว่าวิธีนี้ใช้ได้กับคลาสที่ตัวสร้างทำงานตามปกติเท่านั้น กรณีการใช้งานที่เป็นไปได้: สร้างสำเนาตื้นของคลาสคอนเทนเนอร์ Python มาตรฐาน
__init__
วิธีการของพวกเขา ดังนั้นฉันคิดว่าวิธีนี้อาจดีพอสำหรับวัตถุประสงค์บางอย่าง ไม่ว่าในกรณีใดฉันจะสนใจความเห็นที่เป็นประโยชน์เกี่ยวกับข้อเสนอแนะนี้
class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = arg
ขั้นพื้นฐานตามที่ได้รับ ถ้าฉันfoo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>
หมายความว่าcopy
หน้าที่ของคุณเสียแม้แต่กับพื้นฐานที่สุดของคลาส อีกครั้งเป็นกลอุบาย (ไม่มี DV) แต่ไม่ใช่คำตอบ
copy.copy
วิธีการทำสำเนาตื้น ๆ แต่บางทีไร้เดียงสาฉันคิดว่ามันควรจะเป็นความรับผิดชอบของชั้นเรียนที่จะให้ "constructor คัดลอกตื้น" ในกรณีเช่นนี้ทำไมไม่ให้อินเทอร์เฟซ kinf เดียวกันกับมันdict
และlist
ทำอย่างไร ดังนั้นถ้าชั้นเรียนของคุณต้องการที่จะรับผิดชอบในการคัดลอกวัตถุทำไมไม่เพิ่มif isinstance(arg, type(self))
ประโยคลงไป__init__
?