\[ \newcommand{\NOT}{\neg} \newcommand{\AND}{\wedge} \newcommand{\OR}{\vee} \newcommand{\XOR}{\oplus} \newcommand{\IMP}{\ . #1 \đúng. } \newcommand{\xspace}{} \newcommand{\proofheader}[1]{\underline{\textbf{#1}}} \]
Trong 1. 4 Lưu trữ dữ liệu trong các biến, chúng tôi đã giới thiệu mô hình bộ nhớ dựa trên giá trị để giúp theo dõi các biến và giá trị của chúng
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
5>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
6>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
7>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
8Từ bảng này, chúng ta có thể phỏng đoán rằng có hai biến [
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
5 và >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
7], mỗi biến được liên kết với một giá trị >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
1. Tuy nhiên, bây giờ chúng ta đã biết về việc gán lại và thay đổi, cần có một mô hình bộ nhớ phức tạp hơn. mô hình bộ nhớ dựa trên đối tượng, mà chúng ta sẽ gọi đơn giản là mô hình bộ nhớ Python, vì đây là biểu diễn “tiêu chuẩn” Python lưu trữ dữ liệuĐại diện cho các đối tượng
Nhớ lại rằng mọi phần dữ liệu được lưu trữ trong chương trình Python trong một đối tượng. Nhưng bản thân các đối tượng được lưu trữ như thế nào? . Mỗi vị trí lưu trữ được gắn nhãn với một địa chỉ bộ nhớ duy nhất. Trong Python, mọi đối tượng chúng ta sử dụng được lưu trữ trong bộ nhớ máy tính tại một vị trí cụ thể và trình thông dịch Python có trách nhiệm theo dõi xem đối tượng nào được lưu trữ tại vị trí bộ nhớ nào
Là lập trình viên, chúng tôi không thể kiểm soát địa chỉ bộ nhớ nào được sử dụng để lưu trữ các đối tượng, nhưng chúng tôi có thể truy cập biểu diễn của địa chỉ bộ nhớ này bằng cách sử dụng chức năng
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
2 tích hợp>>> id[3]
1635361280
>>> id['words']
4297547872
Về mặt hình thức, chúng tôi định nghĩa id của một đối tượng Python là mã định danh duy nhất của
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
3 để chỉ đối tượng này. Các chi tiết về cách Python dịch địa chỉ bộ nhớ thành số nguyên không quan trọng đối với chúng tôi. Mọi đối tượng trong Python đều có ba thuộc tính quan trọng—id, giá trị và loại—nhưng trong ba thuộc tính này, chỉ có id của nó được đảm bảo là duy nhấtTrong Python, một biến không phải là một đối tượng và do đó không thực sự lưu trữ dữ liệu; . Chúng tôi cũng nói rằng các biến chứa id của một đối tượng. Đây là trường hợp cho dù dữ liệu là thứ gì đó rất đơn giản như
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
3 hay phức tạp hơn như >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
5. Để làm rõ sự khác biệt này giữa biến và đối tượng, chúng tôi tách chúng thành các phần khác nhau của mô hình bộ nhớ PythonNhư một ví dụ, hãy xem xét mã này
Trong mô hình bộ nhớ dựa trên giá trị của chúng tôi, chúng tôi sẽ biểu diễn các biến này trong một bảng
Với mô hình bộ nhớ Python dựa trên đối tượng đầy đủ, thay vào đó, chúng tôi vẽ một cấu trúc giống như bảng ở bên trái hiển thị ánh xạ giữa các biến và id đối tượng, sau đó là các đối tượng ở bên phải. Mỗi đối tượng được biểu diễn dưới dạng một hộp, với id của nó ở góc trên bên trái, nhập ở góc trên bên phải và giá trị ở giữa. Id đối tượng thực tế được báo cáo bởi hàm
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
2 có nhiều chữ số và giá trị thực của nó không quan trọng; . Vì vậy, đối với các bản vẽ của chúng tôi, chúng tôi tạo các mã định danh ngắn, chẳng hạn như >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
7Vì vậy, không có
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
8 bên trong hộp cho biến >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
9. Thay vào đó, có id của một đối tượng có giá trị là 3. Điều tương tự cũng xảy ra với biến >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
00; Lưu ý rằng chúng tôi đã không vẽ bất kỳ mũi tên nào. Các lập trình viên thường vẽ một mũi tên khi họ muốn chỉ ra rằng một thứ liên quan đến một thứ khác. Điều này thật tuyệt khi bạn rất tự tin với một ngôn ngữ và cách hoạt động của các tham chiếu. Nhưng trong giai đoạn đầu, bạn có nhiều khả năng đưa ra dự đoán chính xác hơn nếu bạn viết ra các tham chiếu [bạn chỉ có thể tạo các giá trị id] thay vì các mũi tên
Câu lệnh gán và đánh giá biểu thức
Bạn đã viết mã phức tạp hơn nhiều so với những gì ở trên, nhưng bây giờ chúng ta có mô hình bộ nhớ Python đầy đủ, chúng ta có thể hiểu thêm một số chi tiết cho các hoạt động cơ bản của Python. Những chi tiết này là nền tảng để viết và sửa lỗi mã phức tạp hơn mà bạn sẽ làm trong năm nay. Vì vậy, hãy tạm dừng một chút và rõ ràng về hai điều
Đánh giá một biểu thức. Đầu tiên, chúng tôi đã nói trước đó rằng việc đánh giá bất kỳ biểu thức Python nào cũng tạo ra một giá trị. Bây giờ chúng ta biết rằng sẽ chính xác hơn khi nói rằng việc đánh giá bất kỳ biểu thức Python nào sẽ tạo ra một id của một đối tượng đại diện cho giá trị của biểu thức. Đối tượng này chính xác là gì phụ thuộc vào loại biểu thức được đánh giá
- Nếu biểu thức là một chữ, chẳng hạn như
02 hoặc>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
03, Python sẽ tạo một đối tượng thuộc loại thích hợp để giữ giá trị>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
- Nếu biểu thức là một biến, Python sẽ tra cứu biến đó. Nếu biến không tồn tại, một
04 được nâng lên. Nếu nó tồn tại, biểu thức tạo ra id được lưu trữ trong biến đó>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
- Nếu biểu thức là một phép toán nhị phân, chẳng hạn như
05 hoặc>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
06, thì trước tiên, Python sẽ đánh giá hai toán hạng của biểu thức và áp dụng toán tử cho các giá trị kết quả, tạo một đối tượng mới thuộc loại phù hợp để giữ giá trị kết quả. Biểu thức tạo id của đối tượng mới>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
- Có các quy tắc bổ sung cho các loại biểu thức khác, nhưng hiện tại những quy tắc này sẽ phù hợp
báo cáo chuyển nhượng. Thứ hai, chúng ta đã nói trước đó rằng một câu lệnh gán được thực thi bằng cách trước tiên ước tính biểu thức bên phải, sau đó lưu trữ nó trong biến bên trái. Đây là một phiên bản chính xác hơn của những gì xảy ra
- Đánh giá biểu thức ở phía bên tay phải, mang lại id của một đối tượng
- Nếu biến ở phía bên trái chưa tồn tại, hãy tạo nó
- Lưu trữ id từ biểu thức ở phía bên tay phải trong biến ở phía bên trái
Biểu diễn dữ liệu ghép
Cho đến nay, các đối tượng duy nhất mà chúng tôi đã xem xét trong mô hình bộ nhớ Python là các thể hiện của các kiểu dữ liệu nguyên thủy. Còn các kiểu dữ liệu phức hợp như bộ sưu tập và lớp dữ liệu thì sao? . Một thể hiện của kiểu dữ liệu phức hợp không lưu trữ giá trị trực tiếp;
Hãy xem điều này có ý nghĩa gì đối với một số loại dữ liệu bộ sưu tập quen thuộc
danh sách. Đây là sơ đồ mô hình bộ nhớ dựa trên đối tượng hiển thị trạng thái của bộ nhớ sau khi thực thi
07>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
Lưu ý rằng có bốn đối tượng riêng biệt trong sơ đồ này. một cho mỗi
3 của>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
09,>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
00 và>>> id[3] 1635361280 >>> id['words'] 4297547872
8, sau đó là một cho chính>>> s = [1, 2] >>> id[s] 1695325453760 >>> s = ['a', 'b'] >>> id[s] 1695325453248
02. Điều này minh họa một trong những sự đánh đổi với mô hình bộ nhớ Python. Nó chính xác hơn mô hình bộ nhớ dựa trên giá trị của chúng tôi, nhưng độ chính xác đó phải trả giá bằng việc có nhiều bộ phận hơn và do đó tốn nhiều thời gian hơn để tạo ra>>> id[3] 1635361280 >>> id['words'] 4297547872
bộ. Đây là sơ đồ mô hình bộ nhớ dựa trên đối tượng cho thấy cách Python đại diện cho tập hợp
03>>> id[3] 1635361280 >>> id['words'] 4297547872
từ điển. Đây là sơ đồ mô hình bộ nhớ dựa trên đối tượng hiển thị từ điển
04. Tổng cộng có năm đối tượng>>> id[3] 1635361280 >>> id['words'] 4297547872
lớp dữ liệu. Tất cả các lớp dữ liệu Python là các kiểu dữ liệu phức hợp và các phiên bản cũng lưu trữ id của các đối tượng khác. Không giống như các loại dữ liệu bộ sưu tập mà chúng ta đã xem xét ở trên, các id này không được đóng gói trong một bộ sưu tập mà thay vào đó, mỗi id được liên kết với một thuộc tính thể hiện cụ thể. Đây là cách chúng tôi đại diện cho đối tượng
05 yêu thích của chúng tôi>>> id[3] 1635361280 >>> id['words'] 4297547872
Bạn có thể nhận thấy một sự khác biệt giữa cách chúng tôi vẽ các hộp đối tượng của nguyên thủy so với. các kiểu dữ liệu phức hợp ở trên. Chúng tôi sẽ sử dụng quy ước vẽ một hộp đôi xung quanh các đối tượng không thay đổi. Hãy coi đó là dấu hiệu rằng bạn không thể vào đó và thay đổi bất cứ điều gì
Trực quan hóa việc gán lại biến và đột biến đối tượng
Chủ đề cuối cùng của chúng ta trong phần này sẽ là sử dụng mô hình bộ nhớ dựa trên đối tượng để trực quan hóa việc gán lại biến và biến đổi đối tượng trong Python
Hãy xem xét trường hợp gán lại biến đơn giản này
Đây là mô hình bộ nhớ của chúng ta trông như thế nào sau khi dòng thứ nhất và dòng thứ hai thực thi
Sử dụng sơ đồ này, chúng ta có thể thấy điều gì sẽ xảy ra khi chúng ta thực hiện việc gán lại
>>> id[3]
1635361280
>>> id['words']
4297547872
06. một đối tượng >>> id[3]
1635361280
>>> id['words']
4297547872
02 mới >>> id[3]
1635361280
>>> id['words']
4297547872
08 được tạo và biến >>> id[3]
1635361280
>>> id['words']
4297547872
09 được gán id của đối tượng mới. Đối tượng danh sách ban đầu >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
00 không bị thay đổi. Việc gán lại biến không làm thay đổi bất kỳ đối tượng nào; . Chúng ta có thể thấy điều này trong trình thông dịch bằng cách sử dụng hàm >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
2 để cho biết đối tượng mà >>> id[3]
1635361280
>>> id['words']
4297547872
09 đề cập đến trước và sau khi gán lại>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
Lưu ý rằng các id khác nhau, cho biết rằng
>>> id[3]
1635361280
>>> id['words']
4297547872
09 đề cập đến một đối tượng mớiTương phản điều này với việc sử dụng một phương thức
>>> id[3]
1635361280
>>> id['words']
4297547872
02 đột biến như >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
05>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
0Trong trường hợp này, không có đối tượng
>>> id[3]
1635361280
>>> id['words']
4297547872
02 mới nào được tạo, mặc dù đối tượng >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
3 mới được. Thay vào đó, đối tượng danh sách >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
00 bị thay đổi và id thứ ba được thêm vào cuối của nó. Lưu ý rằng ngay cả việc thay đổi kích thước của danh sách cũng không thay đổi id của nó. Một lần nữa, chúng ta có thể xác minh rằng >>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
9 đề cập đến cùng một đối tượng >>> id[3]
1635361280
>>> id['words']
4297547872
02 bằng cách kiểm tra id>>> id[3]
1635361280
>>> id['words']
4297547872
0Và cuối cùng, một ví dụ cuối cùng kết hợp chuyển nhượng và đột biến. gán cho một phần của kiểu dữ liệu phức hợp. Hãy xem xét mã này
Điều gì xảy ra trong trường hợp này?
Câu lệnh
>>> s = [1, 2]
>>> id[s]
1695325453760
>>> s = ['a', 'b']
>>> id[s]
1695325453248
51 cũng là một dạng gán lại, nhưng thay vì gán lại một biến, nó gán lại một id là một phần của đối tượng. Điều này có nghĩa là câu lệnh này không thay đổi đối tượng và không gán lại bất kỳ biến nào. Chúng tôi có thể xác minh rằng id của >>> id[3]
1635361280
>>> id['words']
4297547872
09 không thay đổi sau khi gán chỉ mục