Bài tập Python có phải là bản sao hoặc tài liệu tham khảo không?

Trong Python, sao chép danh sách có thể không trực quan đối với người mới bắt đầu. Điều này là do bạn không thể sử dụng toán tử gán để lấy một bản sao độc lập của danh sách. Điều này có thể gây nhầm lẫn, đặc biệt là đối với Pythonistas mới bắt đầu

Để tạo một bản sao độc lập của danh sách trong Python, bạn cần sử dụng hàm deepcopy() của mô-đun sao chép như thế này

import copy

new_list = copy.deepcopy(old_list)

Hướng dẫn toàn diện này hướng dẫn bạn cách lấy một bản sao độc lập của danh sách trong Python. Bạn cũng sẽ tìm hiểu lý do tại sao thực hiện với toán tử gán không hiệu quả

Vấn đề với việc sao chép danh sách trong Python

Khi xử lý các biến Python, bạn có thể tạo một bản sao của một biến bằng cách gán biến hiện có cho một biến mới

Ví dụ

a = 10
b = a

Bây giờ biến b là bản sao của biến a

Nhưng nếu bạn sao chép một danh sách Python theo cách tương tự

new_list = old_list

Mọi sửa đổi được thực hiện đối với new_list cũng thay đổi danh sách ban đầu old_list

Nó xảy ra bởi vì new_list thực sự không phải là bản sao của old_list. Thay vào đó, nó là một tham chiếu đến cùng một đối tượng trong bộ nhớ

Python assignment illustrated“Sao chép” một danh sách chỉ tạo bí danh cho cùng một đối tượng trong bộ nhớ

Để tạo một bản sao hoàn toàn độc lập của danh sách, hãy sử dụng hàm deepcopy() của mô-đun sao chép

import copy

new_list = copy.deepcopy(old_list)

Đây là cách hợp lý duy nhất để tạo một bản sao danh sách hoàn toàn độc lập trong Python

Nhưng nếu bạn không quan tâm đến các bản sao độc lập, thì có nhiều cách khác để tạo một bản sao của danh sách trong Python. đó là

  1. phương thức sao chép (). Tạo một bản sao nông
  2. [. ] toán tử cắt lát. Tạo một bản sao nông
  3. chức năng danh sách (). Tạo một bản sao nông
  4. sao chép. chức năng sao chép (). Tạo một bản sao nông
  5. sao chép. hàm deepcopy(). Tạo một bản sao sâu

Các phần sau đây hướng dẫn bạn cách thức hoạt động của toán tử gán, bản sao nông là gì và tại sao việc sao chép “thất bại” với toán tử gán

Toán tử gán (=) trong Python

Nếu bạn sử dụng toán tử gán (=) để sao chép một danh sách trong Python, thì bạn không thực sự tạo một bản sao của đối tượng. Thay vào đó, bạn chỉ cần tạo ra một biến mới đề cập đến danh sách ban đầu. Biến mới là bí danh của danh sách ban đầu

Hãy xem một ví dụ mà chúng ta

  1. Tạo một danh sách
  2. Gán hoặc “sao chép” danh sách sang một biến mới
  3. Thay đổi số đầu tiên trong danh sách ban đầu
  4. In cả hai danh sách

Và hãy xem chuyện gì xảy ra

Đây là mã

numbers = [1, 2, 3]
new_numbers = numbers

# Only change the original list
numbers[0] = 100

print(numbers)
print(new_numbers)

đầu ra

[100, 2, 3]
[100, 2, 3]

Ở đây bạn chỉ thay đổi phần tử đầu tiên trong danh sách số ban đầu. Nhưng sự thay đổi này cũng diễn ra trong danh sách new_numbers

Điều này xảy ra vì numbers và new_numbers thực sự là cùng một đối tượng danh sách

Assignment in PythonVề cơ bản, cả hai danh sách đều trỏ đến cùng một đốm màu trong bộ nhớ

Một cách khác để xác minh điều này là kiểm tra địa chỉ bộ nhớ của các đối tượng này

Trong Python, bạn có thể sử dụng phương thức id() để tìm ra địa chỉ bộ nhớ của bất kỳ đối tượng nào

Hãy kiểm tra địa chỉ bộ nhớ của cả số và new_numbers

print(id(numbers))
print(id(new_numbers))

đầu ra

140113161420416
140113161420416

ID giống nhau

Điều này xác minh rằng các số và new_numbers là bí danh trỏ đến cùng một đối tượng danh sách trong bộ nhớ

Hãy nghĩ về đối tượng danh sách như một đoạn bộ nhớ không có tên. Các số và new_numbers chỉ là tên mà qua đó bạn có thể truy cập đối tượng danh sách trong bộ nhớ

Vì vậy, khi bạn tạo một biến mới và gán một đối tượng danh sách cho nó, bạn đang giới thiệu một nhãn tham chiếu mới cho đối tượng ban đầu

Tóm lại, toán tử gán (=) tạo một tham chiếu mới đến một đối tượng trong bộ nhớ. Nó không sao chép bất cứ thứ gì. Điều này áp dụng cho danh sách cũng như bất kỳ đối tượng nào khác trong Python

Tiếp theo, hãy xem cách bạn thực sự có thể sao chép các đối tượng danh sách trong Python

Mô-đun sao chép trong Python

Như bạn đã học, bạn không thể sử dụng toán tử gán để sao chép các đối tượng trong Python. Đây là lý do tại sao có một mô-đun riêng, copy dành riêng cho việc sao chép các đối tượng Python

Hai chức năng chính trong mô-đun sao chép là

  • sao chép. sao chép()
  • sao chép. bản sao sâu ()

Chúng ta hãy xem những chức năng này làm gì và sự khác biệt là gì

Bản sao nông. sao chép. sao chép()

Trong Python, một bản sao nông có thể được tạo bằng copy. chức năng sao chép (). Một bản sao nông giải quyết vấn đề sao chép danh sách của chúng tôi theo cách nó không phụ thuộc vào danh sách gốc

Ví dụ

________số 8

đầu ra

[100, 2, 3]
[1, 2, 3]

Như bạn có thể thấy, việc thay đổi phần tử đầu tiên trong danh sách ban đầu không làm thay đổi danh sách đã sao chép

Hãy cũng xác minh các đối tượng không giống nhau bằng cách sử dụng hàm id()

print(id(numbers))
print(id(new_numbers))

đầu ra

a = 10
b = a
1

kinh hoàng. Bây giờ bạn đã biết cách tạo một bản sao nông của danh sách trong Python. Nhưng tâm niệm chữ nông. Điều quan trọng cần lưu ý là đôi khi bạn có thể xử lý một danh sách các danh sách

Trong trường hợp này, bản sao nông không hoạt động theo cách bạn mong đợi. Thay vào đó, nó tạo một bản sao độc lập của danh sách bên ngoài, nhưng các danh sách bên trong được liên kết với danh sách gốc

Tôi biết nó nghe có vẻ khó hiểu

Hãy để tôi chỉ ra điều này có nghĩa là gì bằng cách thực hiện một thử nghiệm đơn giản trong đó tôi

  1. Tạo một danh sách các danh sách
  2. Tạo một bản sao nông của danh sách
  3. Sửa đổi đối tượng đầu tiên của danh sách đầu tiên
a = 10
b = a
2

đầu ra

a = 10
b = a
3

Ở đây, việc thay đổi phần tử đầu tiên của danh sách đầu tiên sẽ ảnh hưởng đến phiên bản đã sao chép của danh sách mặc dù danh sách mới phải là bản sao của danh sách gốc

Nhưng tại sao điều này lại xảy ra?

Trước tiên hãy so sánh ID của các danh sách để xem chúng có phải là cùng một đối tượng không

print(id(numbers))
print(id(new_numbers))

đầu ra

a = 10
b = a
5

Ngay cả ID cũng không khớp. Điều này có nghĩa là new_numbers phải là bản sao thực sự của các số—và đó là

Nhưng tại sao các giá trị vẫn thay đổi trong danh sách đã sao chép?

Điều này là do sao chép. copy() tạo một bản sao nông

Điều này có nghĩa là toàn bộ danh sách được sao chép, nhưng các danh sách bên trong danh sách thì không. Nói cách khác, các danh sách bên trong đề cập đến các danh sách trong danh sách gốc

Tôi biết điều này nghe có vẻ lạ, nhưng đây là cách nó hoạt động

Hãy xác minh điều này bằng cách kiểm tra ID của các danh sách bên trong danh sách

a = 10
b = a
6

đầu ra

a = 10
b = a
7

Như bạn có thể thấy, tất cả ID danh sách bên trong đều giống nhau

Vì vậy, danh sách bên ngoài được sao chép nhưng danh sách bên trong vẫn bị ràng buộc với danh sách danh sách ban đầu

Tóm lại, đây là một minh họa về cách sao chép. copy() hoạt động trên danh sách các danh sách

Shallow copy in Python

Điều này làm nổi bật hành vi sao chép nông trong Python

Như đã nêu trước đó, để tạo một bản sao hoàn toàn độc lập, hãy sử dụng bản sao. hàm deepcopy(). Chúng ta hãy xem xét kỹ hơn chức năng này để xem nó làm gì

Sao chép sâu. sao chép. bản sao sâu ()

Một chức năng quan trọng khác trong mô-đun sao chép là chức năng deepcopy()

Hàm này tạo một bản sao hoàn toàn độc lập của danh sách hoặc bất kỳ đối tượng phức hợp nào khác trong Python

Ví dụ: hãy lặp lại ví dụ trong chương trước bằng cách sử dụng deepcopy()

a = 10
b = a
8

đầu ra

a = 10
b = a
9

Như bạn có thể thấy, việc thay đổi phần tử đầu tiên trong danh sách đầu tiên không ảnh hưởng đến danh sách đã sao chép

Nói cách khác, bạn đã tạo thành công một bản sao hoàn toàn độc lập với danh sách gốc

Deep copy in pythonKhông có phần nào trong danh sách được sao chép sâu trỏ đến danh sách gốc. Do đó, một bản sao sâu tạo ra một bản sao thực sự độc lập

Tôi khuyên bạn nên chơi với các ví dụ để tìm hiểu điều gì đang thực sự xảy ra

Sao chép một đối tượng số trong Python

Hướng dẫn này sẽ không đầy đủ nếu chúng ta không nói về việc sao chép các đối tượng khác ngoài danh sách. Điều quan trọng là nhận ra mọi thứ liên quan đến việc sao chép danh sách áp dụng cho việc sao chép bất kỳ đối tượng Python nào khác

Hãy lặp lại ví dụ đầu tiên trong hướng dẫn này bằng cách sử dụng số nguyên thay vì danh sách

Nói cách khác, hãy

  1. Tạo một biến số
  2. Sao chép số sang một biến khác bằng toán tử gán
  3. Thay đổi số ban đầu
  4. Xem điều gì xảy ra với bản sao
new_list = old_list
0

đầu ra

new_list = old_list
1

Như bạn thấy, thay đổi ban đầu số a không thay đổi số b. Đây có lẽ là điều bạn mong đợi

Nhưng điều này mâu thuẫn với những gì chúng ta đã nói trước đó về việc sao chép các đối tượng Python. Không thể sao chép một đối tượng Python bằng toán tử gán

Tuy nhiên, nhìn vào ví dụ trên, có vẻ như b là một bản sao độc lập của a vì thay đổi a không làm thay đổi b

Mặc dù điều này xảy ra, b không phải là bản sao của a. Điều này là quan trọng để hiểu. Bạn có thể xác minh điều này bằng cách kiểm tra ID của các biến trước khi thay đổi giá trị trong

new_list = old_list
2

đầu ra

new_list = old_list
3

Như bạn có thể thấy, ID phù hợp. Nói cách khác, a và b đều là bí danh của cùng một đối tượng số nguyên trong bộ nhớ

Nhưng tại sao thay đổi a không thay đổi b sau đó?

Tất cả tập trung vào khả năng biến đổi

Trước hết, bạn cần nhớ lại rằng một biến chỉ là một nhãn mà qua đó bạn có thể truy cập một đối tượng nằm ở đâu đó trong bộ nhớ. Vì vậy, nếu bạn thay đổi giá trị của một biến, thì bạn không thực sự thay đổi chính biến đó, mà là đối tượng trong bộ nhớ mà nó tham chiếu đến.

Trong Python, các đối tượng số nguyên là bất biến. Tính bất biến có nghĩa là bạn không thể thực hiện các thay đổi trực tiếp đối với các đối tượng số nguyên. Nếu bạn gán một số nguyên mới cho một biến, thì bạn tạo một đối tượng số nguyên mới trong bộ nhớ và đặt điểm biến hiện tại thành địa chỉ bộ nhớ mới đó

Mặt khác, một danh sách là một đối tượng có thể thay đổi. Điều này có nghĩa là bạn có thể thay đổi trực tiếp đối tượng danh sách. Vì vậy, nếu bạn gán một danh sách hiện có cho một biến mới, bạn đang đặt biến mới trỏ vào danh sách ban đầu

Điều này mô tả ngắn gọn về khả năng biến đổi của Python

Bây giờ, hãy quay lại ví dụ sao chép một số nguyên. Hãy in ID của các biến trước và sau khi thay đổi giá trị trong một

new_list = old_list
4

đầu ra

new_list = old_list
5

ID của biến a và b khớp trước khi gán giá trị mới cho a nhưng không khớp sau đó

Nói cách khác, trước khi thay đổi giá trị trong một

  • a và b trỏ đến cùng một đối tượng số nguyên trong bộ nhớ

Và sau khi thay đổi giá trị trong một

  • a trỏ tới một đối tượng số nguyên mới trong bộ nhớ nhưng b vẫn trỏ tới nơi a đã từng trỏ tới

Vì vậy, sau khi gán một giá trị mới cho biến a, nó trỏ đến một đối tượng số nguyên mới trong bộ nhớ. Điều này xảy ra vì tính bất biến. Đối tượng số nguyên 10 không thể thay đổi trực tiếp. Thay vào đó, một đối tượng số nguyên mới cần được tạo

Dưới đây là một minh họa nhanh về cách thức hoạt động của mã

Python variable copyingGán một số nguyên mới cho a sẽ tạo một đối tượng số nguyên mới trong đó biến a trỏ tới

Tóm lại, không thể sử dụng toán tử gán (=) để sao chép các đối tượng trong Python. Tuy nhiên, khi xử lý các đối tượng bất biến, có vẻ như đây là trường hợp. Nhưng nó không phải như vậy

Nếu ai đó yêu cầu bạn sao chép một biến, về mặt kỹ thuật, bạn cần sử dụng bản sao. sao chép() hoặc sao chép. deepcopy() thay vì toán tử gán

  • Tuy nhiên, khi xử lý các đối tượng bất biến, điều này là không cần thiết, vì hành vi là như nhau bất kể bạn đã sử dụng mô-đun sao chép hay toán tử gán
  • Nhưng với các đối tượng có thể thay đổi, bạn cần sử dụng mô-đun sao chép để tạo một bản sao thực sự của đối tượng

Đến đây thì bạn đã hiểu tại sao toán tử gán không sao chép đối tượng trong Python. Bạn cũng đã học cách sử dụng mô-đun sao chép để tạo bản sao của các đối tượng Python

Bây giờ bạn đã hiểu bản sao nông và bản sao sâu là gì, hãy kết hợp tất cả lại với nhau bằng cách xem 5 cách phổ biến để sao chép danh sách trong Python

5 cách sao chép danh sách trong Python

Có năm cách chính để sao chép một danh sách trong Python

  1. phương thức sao chép ()
  2. [. ] toán tử cắt lát
  3. chức năng danh sách ()
  4. sao chép. chức năng sao chép ()
  5. sao chép. hàm deepcopy()

Hãy xem các ví dụ về từng loại này

1. Phương thức copy()

Kể từ Python 3. 3, một danh sách đi kèm với phương thức copy() tích hợp. Phương pháp này tạo một bản sao nông của danh sách

Ví dụ

new_list = old_list
6

đầu ra

new_list = old_list
7

2. Các [. ] Toán tử cắt lát

Trong Python, cắt nghĩa là kéo một loạt các giá trị từ một lần lặp, chẳng hạn như danh sách

Cắt lát đi với cú pháp của

new_list = old_list
8

Nơi bắt đầu chỉ định chỉ mục bắt đầu và kết thúc chỉ định chỉ mục kết thúc

Nếu bạn không chỉ định tham số bắt đầu, việc cắt sẽ bắt đầu từ phần tử đầu tiên. Nếu bạn không chỉ định kết thúc, quá trình cắt sẽ kết thúc ở phần tử cuối cùng

Gọi iterable[. ] trả về một lát đại diện cho toàn bộ lần lặp. Nói cách khác, nó trả về một bản sao của danh sách khi được gọi trong danh sách

Lưu ý rằng điều này cũng tạo ra một bản sao nông

Ví dụ

new_list = old_list
9

đầu ra

new_list = old_list
7

3. Hàm danh sách ()

Để chuyển đổi một đối tượng thành một danh sách trong Python, bạn có thể sử dụng hàm list() tích hợp sẵn. Hàm này tạo một đối tượng danh sách mới cho đối số đầu vào

Khi bạn gọi hàm list() trên một danh sách trong Python, bạn buộc nó tạo một bản sao của danh sách gốc. Kiểu chép này cũng cạn

Ví dụ

import copy

new_list = copy.deepcopy(old_list)
1

đầu ra

new_list = old_list
7

4. Bản sao chép. chức năng sao chép ()

Như đã thảo luận trước đó trong hướng dẫn này, có một bản sao mô-đun chuyên dụng để sao chép các đối tượng Python

Một trong những chức năng trong mô-đun này là chức năng copy(). Hàm này tạo một bản sao nông của một đối tượng Python. Bạn có thể sử dụng bản sao. copy() để tạo một bản sao của danh sách

Ví dụ

import copy

new_list = copy.deepcopy(old_list)
3

đầu ra

new_list = old_list
7

5. Bản sao chép. hàm deepcopy()

Cách duy nhất để tạo một bản sao sâu thực sự độc lập của một đối tượng Python là sử dụng lệnh copy. hàm deepcopy()

Sự khác biệt giữa bản sao nông và bản sao sâu chỉ liên quan đến các đối tượng bao gồm các đối tượng. Điều này được giải thích toàn diện trước đó trong hướng dẫn này

Bạn có thể sử dụng bản sao. deepcopy() để tạo một bản sao sâu của danh sách

Ví dụ

import copy

new_list = copy.deepcopy(old_list)
5

đầu ra

new_list = old_list
7

Phần kết luận

Hôm nay bạn đã học cách sao chép thành công danh sách Python

Tóm lại, không thể sao chép bằng toán tử gán. Thay vì sao chép, nó tạo một bí danh mới cho đối tượng ban đầu. Điều này có nghĩa là thay đổi đối tượng ban đầu cũng thay đổi “bản sao”

Python có sao chép theo tham chiếu không?

Python truyền các đối số không phải theo tham chiếu hay theo giá trị mà theo phép gán .

Nhiệm vụ có nghĩa là gì trong Python?

Toán tử gán, được biểu thị bằng ký hiệu “=”, là toán tử được sử dụng để gán giá trị cho các biến trong Python . Dòng x=1 lấy giá trị đã biết là 1 và gán giá trị đó cho biến có tên “x”. Sau khi thực hiện dòng này, số này sẽ được lưu vào biến này.

Toán tử gán có sao chép sâu không?

Giống như hàm tạo sao chép, toán tử gán phải tạo một bản sao của đối tượng. Phiên bản mặc định tạo ra một bản sao nông . Nếu muốn có một bản sao sâu cho các bài tập về loại do người dùng xác định (e. g. một lớp), thì toán tử gán sẽ được nạp chồng cho lớp.

Bài tập trong Python với ví dụ là gì?

1) Chỉ định. Toán tử này được sử dụng để gán giá trị của vế phải của biểu thức cho toán hạng bên trái. . Toán tử gán trong Python