Đối tượng có thể thay đổi và bất biến trong Python - Hướng dẫn trực quan và thực hành

Python là một ngôn ngữ tuyệt vời. Vì tính đơn giản của nó, nhiều người chọn nó làm ngôn ngữ lập trình đầu tiên của họ.

Các lập trình viên có kinh nghiệm cũng sử dụng Python, nhờ vào cộng đồng rộng lớn, nhiều gói và cú pháp rõ ràng.

Nhưng có một vấn đề dường như gây nhầm lẫn cho người mới bắt đầu cũng như một số nhà phát triển có kinh nghiệm: các đối tượng Python. Cụ thể là sự khác biệt giữa các đối tượng có thể thay đổibất biến .

Trong bài đăng này, chúng ta sẽ đào sâu kiến ​​thức về các đối tượng Python, tìm hiểu sự khác biệt giữa các đối tượng có thể thay đổikhông thể thay đổi , và xem cách chúng ta có thể sử dụng trình thông dịch để hiểu rõ hơn cách Python hoạt động.

Chúng tôi sẽ sử dụng các chức năng và từ khóa quan trọng như idis, và chúng tôi sẽ hiểu sự khác biệt giữa x == yx is y.

Bạn có cho nó? Bắt đầu nào.

Trong Python, mọi thứ đều là một đối tượng

Không giống như các ngôn ngữ lập trình khác mà ngôn ngữ này hỗ trợ các đối tượng, trong Python thực sự mọi thứ đều là một đối tượng - bao gồm cả số nguyên, danh sách và thậm chí là các hàm.

Chúng tôi có thể sử dụng thông dịch viên của mình để xác minh rằng:

>>> isinstance(1, object) True >>> isinstance(False, object) True def my_func(): return "hello" >>> isinstance(my_func, object) True

Python có một hàm tích hợp, hàm idnày trả về địa chỉ của một đối tượng trong bộ nhớ. Ví dụ:

>>> x = 1 >>> id(x) 1470416816

Ở trên, chúng ta đã tạo một đối tượng theo tên của xvà gán cho nó giá trị là 1. Sau đó chúng tôi đã sử dụng id(x)và phát hiện ra rằng đối tượng này được tìm thấy tại địa chỉ 1470416816trong bộ nhớ.

Điều này cho phép chúng tôi kiểm tra những điều thú vị về Python. Giả sử chúng ta tạo hai biến trong Python - một có tên là xvà một có tên là y- và gán chúng cùng một giá trị. Ví dụ, ở đây:

>>> x = "I love Python!" >>> y = "I love Python!"

Chúng ta có thể sử dụng toán tử bình đẳng ( ==) để xác minh rằng chúng thực sự có cùng giá trị trong mắt Python:

>>> x == y True

Nhưng đây có phải là cùng một đối tượng trong bộ nhớ? Về lý thuyết, có thể có hai kịch bản rất khác nhau ở đây.

Theo kịch bản (1) , chúng ta thực sự có hai đối tượng khác nhau, một đối tượng có tên là xvà một đối tượng khác có tên là y, tình cờ có cùng giá trị.

Tuy nhiên, nó cũng có thể là trường hợp Python thực sự chỉ lưu trữ ở đây một đối tượng, có hai tên tham chiếu đến nó - như được hiển thị trong kịch bản (2) :

Chúng ta có thể sử dụng idchức năng được giới thiệu ở trên để kiểm tra điều này:

>>> x = "I love Python!" >>> y = "I love Python!" >>> x == y True >>> id(x) 52889984 >>> id(y) 52889384

Vì vậy, như chúng ta có thể thấy, hành vi của Python phù hợp với kịch bản (1) được mô tả ở trên. Mặc dù x == ytrong ví dụ này (nghĩa là xycó cùng giá trị ), chúng là các đối tượng khác nhau trong bộ nhớ. Điều này là do id(x) != id(y), như chúng tôi có thể xác minh một cách rõ ràng:

>>> id(x) == id(y) False

Có một cách ngắn hơn để thực hiện so sánh ở trên, đó là sử dụng istoán tử của Python . Kiểm tra xem x is ycó giống với kiểm tra hay không id(x) == id(y), nghĩa là có xycó cùng một đối tượng trong bộ nhớ hay không:

>>> x == y True >>> id(x) == id(y) False >>> x is y False

Điều này làm sáng tỏ sự khác biệt quan trọng giữa toán tử bình đẳng ==và toán tử nhận dạng is.

Như bạn có thể thấy trong ví dụ trên, nó là hoàn toàn có thể cho hai tên bằng Python ( xy) bị ràng buộc với hai đối tượng khác nhau (và do đó, x is yFalse), nơi hai đối tượng này có cùng giá trị (do đó x == yTrue).

Làm thế nào chúng ta có thể tạo một biến khác trỏ đến cùng một đối tượng xđang trỏ đến? Chúng ta chỉ có thể sử dụng toán tử gán =, như sau:

>>> x = "I love Python!" >>> z = x

Để xác minh rằng chúng thực sự trỏ đến cùng một đối tượng, chúng ta có thể sử dụng istoán tử:

>>> x is z True

Tất nhiên, điều này có nghĩa là chúng có cùng địa chỉ trong bộ nhớ, vì chúng ta có thể xác minh rõ ràng bằng cách sử dụng id:

>>> id(x) 54221824 >>> id(z) 54221824

Và tất nhiên, chúng có cùng giá trị, vì vậy chúng tôi cũng mong đợi x == zsẽ trả về True:

>>> x == z True

Các đối tượng có thể thay đổi và bất biến trong Python

Chúng tôi đã nói rằng mọi thứ trong Python đều là một đối tượng, nhưng có một sự khác biệt quan trọng giữa các đối tượng. Một số đối tượng có thể thay đổi trong khi một số là bất biến .

Như tôi đã đề cập trước đây, thực tế này gây ra sự nhầm lẫn cho nhiều người mới sử dụng Python, vì vậy chúng tôi sẽ đảm bảo rằng nó rõ ràng.

Đối tượng bất biến trong Python

Đối với một số kiểu trong Python, khi chúng ta đã tạo các phiên bản của các kiểu đó, chúng sẽ không bao giờ thay đổi. Chúng là bất biến .

Ví dụ, intcác đối tượng là bất biến trong Python. Điều gì sẽ xảy ra nếu chúng ta cố gắng thay đổi giá trị của một intđối tượng?

>>> x = 24601 >>> x 24601 >>> x = 24602 >>> x 24602

Chà, có vẻ như chúng tôi đã thay đổi xthành công. Đây chính là điểm khiến nhiều người nhầm lẫn. Chính xác thì điều gì đã xảy ra ở đây? Hãy sử dụng idđể điều tra thêm:

>>> x = 24601 >>> x 24601 >>> id(x) 1470416816 >>> x = 24602 >>> x 24602 >>> id(x) 1470416832

Vì vậy, chúng ta có thể thấy rằng bằng cách gán x = 24602, chúng ta đã không thay đổi giá trị của đối tượng xđã bị ràng buộc trước đó. Thay vào đó, chúng tôi đã tạo một đối tượng mới và gắn tên xvới nó.

Vì vậy, sau khi gán 24601cho xbằng cách sử dụng x = 24601, chúng tôi có trạng thái sau:

Và sau khi sử dụng x = 24602, chúng tôi đã tạo một đối tượng mới và gắn tên xvới đối tượng mới này. Đối tượng khác có giá trị 24601là không còn có thể truy cập được bằng x(hoặc bất kỳ tên nào khác trong trường hợp này):

Whenever we assign a new value to a name (in the above example - x) that is bound to an int object, we actually change the binding of that name to another object.

The same applies for tuples, strings (str objects), and bools as well. In other words, int (and other number types such as float), tuple, bool, and str objects are immutable.

Let's test this hypothesis. What happens if we create a tuple object, and then give it a different value?

>>> my_tuple = (1, 2, 3) >>> id(my_tuple) 54263304 >>> my_tuple = (3, 4, 5) >>> id(my_tuple) 56898184

Just like an int object, we can see that our assignment actually changed the object that the name my_tuple is bound to.

What happens if we try to change one of the tuple's elements?

>>> my_tuple[0] = 'a new value' Traceback (most recent call last): File "", line 1, in  TypeError: 'tuple' object does not support item assignment

As we can see, Python doesn't allow us to modify my_tuple's contents, as it is immutable.

Mutable objects in Python

Some types in Python can be modified after creation, and they are called mutable. For example, we know that we can modify the contents of a list object:

>>> my_list = [1, 2, 3] >>> my_list[0] = 'a new value' >>> my_list ['a new value', 2, 3]

Does that mean we actually created a new object when assigning a new value to the first element of my_list? Again, we can use id to check:

>>> my_list = [1, 2, 3] >>> id(my_list) 55834760 >>> my_list [1, 2, 3] >>> my_list[0] = 'a new value' >>> id(my_list) 55834760 >>> my_list ['a new value', 2, 3]

So our first assignment my_list = [1, 2, 3] created an object in the address 55834760, with the values of 1, 2, and 3:

We then modified the first element of this list object using my_list[0] = 'a new value', that is - without creating a new list object:

Now, let us create two names – x and y, both bound to the same list object. We can verify that either by using is, or by explicitly checking their ids:

>>> x = y = [1, 2] >>> x is y True >>> id(x) 18349096 >>> id(y) 18349096 >>> id(x) == id(y) True

What happens now if we use x.append(3)? That is, if we add a new element (3) to the object by the name of x?

Will x by changed? Will y?

Well, as we already know, they are basically two names of the same object:

Since this object is changed, when we check its names we can see the new value:

>>> x.append(3) >>> x [1, 2, 3] >>> y [1, 2, 3]

Note that x and y have the same id as before – as they are still bound to the same list object:

>>> id(x) 18349096 >>> id(y) 18349096

In addition to lists, other Python types that are mutable include sets and dicts.

Implications for dictionary keys in Python

Dictionaries (dict objects) are commonly used in Python. As a quick reminder, we define them like so:

my_dict = {"name": "Omer", "number_of_pets": 1}

We can then access a specific element by its key name:

>>> my_dict["name"] 'Omer'

Dictionaries are mutable, so we can change their content after creation. At any given moment, a key in the dictionary can point to one element only:

>>> my_dict["name"] = "John" >>> my_dict["name"] 'John'

It is interesting to note that a dictionary's keys must be immutable:

>>> my_dict = {[1,2]: "Hello"} Traceback (most recent call last): File "", line 1, in  TypeError: unhashable type: 'list'

Why is that so?

Let's consider the following hypothetical scenario (note: the snippet below can't really be run in Python):

>>> x = [1, 2] >>> y = [1, 2, 3] >>> my_dict = {x: 'a', y: 'b'}

So far, things don't seem that bad. We'd assume that if we access my_dict with the key of [1, 2], we will get the corresponding value of 'a', and if we access the key [1, 2, 3], we will get the value 'b'.

Now, what would happen if we attempted to use:

>>> x.append(3)

In this case, x would have the value of [1, 2, 3], and y would also have the value of [1, 2, 3]. What should we get when we ask for my_dict[[1, 2, 3]]? Will it be 'a' or 'b'? To avoid such cases, Python simply doesn't allow dictionary keys to be mutable.

Taking things a bit further

Let's try to apply our knowledge to a case that is a bit more interesting.

Below, we define a list (a mutable object) and a tuple (an immutable object). The list includes a tuple, and the tuple includes a list:

>>> my_list = [(1, 1), 2, 3] >>> my_tuple = ([1, 1], 2, 3) >>> type(my_list)  >>> type(my_list[0])  >>> type(my_tuple)  >>> type(my_tuple[0]) 

So far so good. Now, try to think for yourself – what will happen when we try to execute each of the following statements?

(1) >>> my_list[0][0] = 'Changed!'

(2) >>> my_tuple[0][0] = 'Changed!'

In statement (1), what we are trying to do is change my_list's first element, that is, a tuple. Since a tuple is immutable, this attempt is destined to fail:

>>> my_list[0][0] = 'Changed!' Traceback (most recent call last): File "", line 1, in  TypeError: 'tuple' object does not support item assignment

Note that what we were trying to do is not change the list, but rather – change the contents of its first element.

Let's consider statement (2). In this case, we are accessing my_tuple's first element, which happens to be a list, and modify it. Let's further investigate this case and look at the addresses of these elements:

>>> my_tuple = ([1, 1], 2, 3) >>> id(my_tuple) 20551816 >>> type(my_tuple[0])  >>> id(my_tuple[0]) 20446248

When we change my_tuple[0][0], we do not really change my_tuple at all! Indeed, after the change, my_tuple's first element will still be the object whose address in memory is 20446248. We do, however, change the value of that object:

>>> my_tuple[0][0] = 'Changed!' >>> id(my_tuple) 20551816 >>> id(my_tuple[0]) 20446248 >>> my_tuple (['Changed!', 1], 2, 3)

Since we only modified the value of my_tuple[0], which is a mutable list object, this operation was indeed allowed by Python.

Recap

In this post we learned about Python objects. We said that in Python everything is an object, and got to use id and is to deepen our understanding of what's happening under the hood when using Python to create and modify objects.

We also learned the difference between mutable objects, that can be modified after creation, and immutable objects, which cannot.

We saw that when we ask Python to modify an immutable object that is bound to a certain name, we actually create a new object and bind that name to it.

Sau đó, chúng tôi đã tìm hiểu tại sao các khóa từ điển phải là bất biến trong Python.

Hiểu cách Python "nhìn thấy" các đối tượng là chìa khóa để trở thành một lập trình viên Python tốt hơn. Tôi hy vọng bài đăng này đã giúp bạn trên hành trình làm chủ Python.

Omer Rosenbaum , Giám đốc Công nghệ của Swimm . Chuyên gia đào tạo không gian mạng và Người sáng lập Học viện An ninh Checkpoint. Tác giả của Mạng máy tính (bằng tiếng Do Thái) . Ghé thăm kênh YouTube của tôi .