Bài viết này giải thích cách bạn có thể sao chép các đối tượng bất biến trong Python từ biến này sang biến khác bằng cách sử dụng phương pháp sao chép nông và sao chép sâu
Các đối tượng Python có thể là bất biến [số nguyên, float, bool, tuple, v.v. ] hoặc có thể thay đổi [danh sách, từ điển, bộ]. Vì không thể cập nhật các đối tượng bất biến nên việc sao chép chúng luôn sao chép vị trí bộ nhớ của chúng
Đối với các đối tượng có thể thay đổi, bạn có ba cách chính để sao chép chúng. sử dụng toán tử gán, sao chép nông một đối tượng hoặc sao chép sâu một đối tượng. Bạn sẽ nghiên cứu ba cách tiếp cận này trong hướng dẫn này
Sao chép qua Toán tử chuyển nhượng
Trước khi chúng tôi nghiên cứu sự khác biệt giữa sao chép nông và sao chép sâu các đối tượng Python, điều quan trọng là phải hiểu cách toán tử gán [=] gán giá trị cho các biến
Toán tử gán chỉ cần gán tham chiếu bộ nhớ của một đối tượng cho một đối tượng khác thay vì tạo các bản sao mới của các giá trị được lưu trữ bởi một biến. Hãy xem một ví dụ
Đoạn script sau định nghĩa một danh sách,num1 và gán biến danh sách này tonums2
nums1 = [10, 20, 30, 40, 50]
nums2 = nums1
print[nums2]
print[nums1]
đầu ra
[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
Nếu bạn nhìn vào vị trí bộ nhớ của cả hai biến thenums1andnums2, bạn sẽ thấy rằng chúng trỏ đến vị trí bộ nhớ giống hệt nhau
print[hex[id[nums1]]]
print[hex[id[nums2]]]
đầu ra
0x1560e31c7c0
0x1560e31c7c0
Điều này có nghĩa là khi bạn cập nhật một giá trị trong bất kỳ biến nào trong hai biến danh sách, thay đổi đó sẽ được phản ánh trong danh sách kia. Ví dụ: nếu bạn gán giá trị 500 cho chỉ mục thứ hai của thenums2list, bạn sẽ nhận thấy rằng chỉ mục thứ hai của thenums1list cũng được cập nhật
nums2[2] = 500
print[nums2]
print[nums1]
đầu ra
[10, 20, 500, 40, 50]
[10, 20, 500, 40, 50]
Đây là một khái niệm quan trọng cần hiểu và bạn có thể chưa nhận ra
Nhận miễn phí Bộ công cụ dành cho nhà phát triển Python của chúng tôi
Tôi đã tập hợp Bộ công cụ dành cho nhà phát triển Python với hơn 100 tập lệnh Python được tạo sẵn bao gồm cấu trúc dữ liệu, Pandas, NumPy, Seaborn, máy học, xử lý tệp, quét web và nhiều thứ khác - và tôi muốn bạn có bộ công cụ này miễn phí. Nhập địa chỉ email của bạn dưới đây và tôi sẽ gửi một bản sao theo cách của bạn
Python đi kèm với một mô-đun có tên là
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
7 cung cấp chức năng sao chép nhất định. Trong bài viết này, chúng ta sẽ khám phá những bản sao sâu và nông là gì. Ngoài ra, chúng ta sẽ thảo luận về sự khác biệt của chúng và khi nào nên sử dụng cái này hơn cái kiaCác đối tượng không thể thay đổi so với có thể thay đổi
Trước khi chuyển sang các bản sao nông và sâu trong Python, điều quan trọng trước tiên là phải hiểu sự khác biệt giữa các loại đối tượng có thể thay đổi và không thể thay đổi. Như tên cho thấy, các đối tượng bất biến không thể bị thay đổi và do đó, thay vào đó, Python sẽ tạo một đối tượng mới khi giá trị của đối tượng đó bị sửa đổi
Chẳng hạn, giả sử rằng chúng ta có hai biến tham chiếu đến cùng một đối tượng số nguyên
>>> a = 10
>>> b = a # variables a and b hold the reference to the same object
Bây giờ nếu chúng ta thực hiện bất kỳ loại thao tác nào trên biến
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8— và cho rằng các số nguyên trong Python là bất biến — kết quả về cơ bản sẽ là một đối tượng mới chứa giá trị mới. Điều này có nghĩa là giá trị cũ của đối tượng [và do đó tất cả các biến tham chiếu đến nó], sẽ không thay đổi>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
Mặt khác, các loại đối tượng có thể thay đổi cho phép sửa đổi tại chỗ giá trị đối tượng. Điều này có nghĩa là khi giá trị của một loại đối tượng có thể thay đổi được sửa đổi, sẽ có tác động đến tất cả các biến đang giữ tham chiếu đến cùng một đối tượng. Chẳng hạn, giả sử chúng ta có các danh sách sau
[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
0Vì các danh sách trong Python có thể thay đổi, nếu chúng ta thay đổi bất kỳ danh sách nào trong hai danh sách thì hành động này cũng sẽ có tác động trực tiếp đến biến kia, vì cả hai đều trỏ đến cùng một tham chiếu đối tượng trong bộ nhớ
[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
1Để biết thêm chi tiết về mô hình Dynamic Typing của Python, bạn có thể đọc một trong những bài viết của tôi tại đây trên Medium
Nhập động trong Python
là gốc rễ của tính linh hoạt của ngôn ngữ
hướng tới khoa học dữ liệu. com
Bài tập bình thường
Cách tiếp cận đơn giản nhất để sao chép các đối tượng là thông qua các hoạt động gán thông thường. Giả sử rằng chúng ta có các thao tác sau
[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
2Trong trường hợp này, cả hai biến
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8 và [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
00 đều giữ cùng một tham chiếu đến cùng một đối tượng. Điều này có nghĩa là nếu bất kỳ biến nào trong hai biến được sử dụng để thực hiện sửa đổi tại chỗ, thì biến còn lại cũng sẽ bị ảnh hưởng[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
5Do đó, các hoạt động gán bình thường thường được sử dụng khi chúng ta phải xử lý các loại đối tượng không thay đổi. Trong trường hợp này, khi một thao tác được thực hiện bằng bất kỳ biến nào trong hai biến, biến còn lại sẽ không thay đổi vì tham chiếu của nó đang hướng tới đối tượng cũ cũng không thay đổi, với điều kiện là nó không thay đổi
[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
6Câu lệnh gán trong Python không sao chép đối tượng, chúng tạo liên kết giữa mục tiêu và đối tượng — Python Docs
Bản sao nông vs Bản sao sâu
Trước khi đi sâu vào chi tiết của các bản sao nông và sâu, hãy lưu ý rằng sự khác biệt của chúng chỉ liên quan khi chúng ta phải xử lý các đối tượng phức hợp về cơ bản là các cấu trúc lồng nhau. Nói cách khác, các đối tượng ghép là các đối tượng chứa các đối tượng khác — ví dụ: danh sách các danh sách hoặc từ điển các tập hợp
Một bản sao nông sẽ lấy một bản sao của đối tượng ban đầu và tạo một đối tượng ghép mới nhưng nếu đối tượng chúng ta đang sao chép là một đối tượng ghép thì các đối tượng bên trong sẽ giống như các đối tượng được tìm thấy trong đối tượng ban đầu
[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
7Như chúng ta có thể thấy, các đối tượng danh sách
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8 và [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
00 khác nhau, nghĩa là chúng chứa các tham chiếu khác nhau trỏ đến các đối tượng khác nhau trong bộ nhớ [mặc dù giá trị của các đối tượng này giống nhau]Mọi thứ trở nên phức tạp hơn một chút khi chúng ta cần xử lý các đối tượng phức hợp. Bây giờ, hãy giả sử rằng biến
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8 là một đối tượng phức hợp đại diện cho một danh sách các danh sáchprint[hex[id[nums1]]]
print[hex[id[nums2]]]
1Bây giờ chúng tôi lấy một bản sao nông của
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8print[hex[id[nums1]]]
print[hex[id[nums2]]]
3và chúng ta có thể thấy rằng
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8 và [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
00 là các đối tượng khác nhau>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
0tuy nhiên, các đối tượng bên trong [i. e. hai danh sách bên trong] giống với danh sách được tham chiếu bởi đối tượng ban đầu
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
1Điều này khá nguy hiểm vì sự thay đổi trong bất kỳ danh sách bên trong nào sẽ ảnh hưởng đến đối tượng phức hợp khác tham chiếu đến các danh sách bên trong này
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
2Do đó, một bản sao nông chỉ phù hợp khi chúng ta không phải xử lý các đối tượng ghép
Một bản sao nông xây dựng một đối tượng phức hợp mới và sau đó [trong phạm vi có thể] chèn các tham chiếu vào đó tới các đối tượng được tìm thấy trong bản gốc — Tài liệu Python
Một bản sao sâu sẽ lấy một bản sao của đối tượng ban đầu và sau đó sẽ sao chép đệ quy các đối tượng bên trong được tìm thấy [nếu có]
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
3Một lần nữa, chúng ta có thể thấy rằng đối tượng gốc và đối tượng sao chép về cơ bản là khác nhau
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
0nhưng trong trường hợp này, ngay cả các đối tượng bên trong cũng sẽ khác
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
5Điều này có nghĩa là thay đổi trong bất kỳ danh sách lồng nhau nào trong
>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
8 sẽ không ảnh hưởng đến danh sách tương ứng trong đối tượng [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
00>>> a = a + 1
>>> print[a]
11
>>> print[b]
10
6Do đó, một bản sao sâu sẽ phù hợp hơn khi chúng ta phải xử lý các đối tượng hỗn hợp và muốn đảm bảo rằng sự thay đổi trong bất kỳ đối tượng bên trong nào sẽ không ảnh hưởng đến các biến khác tham chiếu đến cùng một đối tượng
Một bản sao sâu xây dựng một đối tượng phức hợp mới và sau đó, theo cách đệ quy, chèn các bản sao của các đối tượng được tìm thấy trong bản gốc vào đó — Tài liệu Python
Phần kết luận
Trong bài viết này, chúng ta đã khám phá ba cách cơ bản mà một người có thể sử dụng để sao chép các đối tượng trong Python. Ban đầu, chúng tôi đã thảo luận về sự khác biệt giữa các loại đối tượng không thay đổi và có thể thay đổi. Không cần thiết phải sao chép các loại đối tượng bất biến vì giá trị của các trường hợp đó sẽ không bao giờ thay đổi. Mặt khác, các nhà phát triển cần cẩn thận khi sửa đổi các loại đối tượng có thể thay đổi vì hành động này có khả năng ảnh hưởng đến các biến khác đang giữ tham chiếu đến cùng một đối tượng. Khi đối tượng như vậy được thay đổi tại chỗ, thì tất cả các biến khác tham chiếu đến cùng một đối tượng cũng sẽ bị ảnh hưởng bởi thay đổi này
Do đó, điều quan trọng là phải hiểu cách sao chép đúng các đối tượng có thể thay đổi để tránh các lỗi trong mã của bạn. Nhớ lại rằng một bản sao nông sẽ tạo ra một đối tượng mới từ bản gốc, tuy nhiên nếu đối tượng chứa các đối tượng khác thì các đối tượng bên trong sẽ không được sao chép mà thay vào đó, cùng một tham chiếu sẽ được sử dụng làm cấu trúc ban đầu. Mặt khác, một bản sao sâu sẽ tạo ra một đối tượng mới ngay cả đối với các đối tượng bên trong chứa trong một đối tượng phức hợp