Làm thế nào tôi có thể tạo một bản sao của một đối tượng trong Python?
Vì vậy, nếu tôi thay đổi giá trị của các trường của đối tượng mới, đối tượng cũ sẽ không bị ảnh hưởng bởi điều đó.
Bạn có nghĩa là một đối tượng đột biến sau đó.
Trong Python 3, danh sách có một copy
phương thức (trong 2, bạn sẽ sử dụng một lát để tạo một bản sao):
>>> 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
Bản sao nông
Bản sao nông chỉ là bản sao của thùng chứa ngoài cùng.
list.copy
là một bản sao nông:
>>> 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'}}]
Bạn không nhận được một bản sao của các đối tượng bên trong. Chúng là cùng một đối tượng - vì vậy khi chúng bị đột biến, sự thay đổi sẽ xuất hiện trong cả hai container.
Bản sao sâu
Bản sao sâu là bản sao đệ quy của từng đối tượng nội thất.
>>> 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'}}]
Thay đổi không được phản ánh trong bản gốc, chỉ trong bản sao.
Đối tượng bất biến
Đối tượng bất biến thường không cần phải được sao chép. Trong thực tế, nếu bạn cố gắng, Python sẽ chỉ cung cấp cho bạn đối tượng ban đầu:
>>> 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 thậm chí không có phương pháp sao chép, vì vậy hãy thử với một lát:
>>> tuple_copy_attempt = a_tuple[:]
Nhưng chúng ta thấy đó là cùng một đối tượng:
>>> tuple_copy_attempt is a_tuple
True
Tương tự cho chuỗi:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
và đối với hàng chục người, mặc dù họ có một copy
phương pháp:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Khi nào cần sao chép các đối tượng bất biến
Các đối tượng bất biến nên được sao chép nếu bạn cần một đối tượng bên trong có thể thay đổi được sao chép.
>>> 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'],)
Như chúng ta có thể thấy, khi đối tượng bên trong của bản sao bị đột biến, bản gốc không thay đổi.
Đối tượng tùy chỉnh
Các đối tượng tùy chỉnh thường lưu trữ dữ liệu trong một __dict__
thuộc tính hoặc trong __slots__
(cấu trúc bộ nhớ giống như tuple.)
Để tạo một đối tượng có thể __copy__
sao chép , xác định (đối với các bản sao nông) và / hoặc __deepcopy__
(đối với các bản sao sâu).
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
Lưu ý rằng deepcopy
giữ một từ điển ghi nhớ của id(original)
(hoặc số nhận dạng) cho các bản sao. Để tận hưởng hành vi tốt với các cấu trúc dữ liệu đệ quy, hãy đảm bảo bạn chưa tạo một bản sao và nếu có, hãy trả lại.
Vì vậy, hãy tạo một đối tượng:
>>> c1 = Copyable(1, [2])
Và copy
tạo một bản sao nông:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
Và deepcopy
bây giờ tạo một bản sao sâu sắc:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]