Các biến a và b là các đối tượng khác nhau mặc dù chúng có cùng giá trị. Do đó, so sánh các giá trị với toán tử == trả về True nhưng kiểm tra xem các biến có tham chiếu đến cùng một đối tượng hay không sẽ trả về Sai
Trong hướng dẫn này, bạn sẽ tìm hiểu câu lệnh is là gì, tại sao nó quan trọng và khi nào bạn nên sử dụng nó
Nhận dạng đối tượng trong Python
Trong Python, hai đối tượng có cùng giá trị không có nghĩa là các đối tượng sẽ giống hệt nhau. Tương tự như trong đời thực, hai người có cùng tên không có nghĩa họ là cùng một người
Bất cứ khi nào bạn tạo một đối tượng Python, Python sẽ lưu nó vào bộ nhớ phía sau một địa chỉ bộ nhớ cụ thể. Mỗi đối tượng có địa chỉ duy nhất của riêng mình trong bộ nhớ
Bạn có thể kiểm tra địa chỉ bộ nhớ của bất kỳ đối tượng Python nào bằng hàm id[] tích hợp. Nó trả về một giá trị số nguyên đại diện cho địa chỉ bộ nhớ của đối tượng
Ví dụ
>>> a = 1000 >>> id[a] 140053230323952
Bây giờ, nếu bạn tạo hai đối tượng có cùng giá trị, các đối tượng vẫn kết thúc ở các vị trí bộ nhớ khác nhau. Bạn có thể xác minh điều này bằng cách kiểm tra ID của các đối tượng
Ví dụ
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False
Ở đây bạn có thể thấy rằng các ID khác nhau
Trong Python, có một cơ chế tích hợp để kiểm tra xem ID của hai đối tượng có bằng nhau không. Đây là câu lệnh is
Câu lệnh is trong Python
Câu lệnh is trong Python kiểm tra xem hai đối tượng có giống hệt nhau không. Nói cách khác, nó kiểm tra xem hai đối tượng có nằm trong cùng một địa chỉ bộ nhớ hay không, nghĩa là, nếu các đối tượng có cùng ID
Câu lệnh is trả về True nếu các đối tượng có cùng ID, ngược lại là False
Với câu lệnh is, bạn có thể thay thế đoạn mã này
id[obj1] == id[obj2]
Với
obj1 is obj2
Từ ví dụ của chương trước, bạn có thể thay thế
>>> id[n1] == id[n2] False
Với
>>> n1 is n2 False
Bây giờ bạn đã hiểu sự khác biệt giữa toán tử đẳng thức == và câu lệnh is trong Python là gì. Tiếp theo, chúng ta hãy xem làm thế nào các biến chỉ là bí danh của các đối tượng đằng sau hậu trường và nó ảnh hưởng đến danh tính của các biến như thế nào
Biến là bí danh trong Python
Bạn có thể coi biến Python là tên gắn liền với một đối tượng. Một đối tượng Python có thể có nhiều biến tham chiếu đến cùng một đối tượng. Do đó, mỗi biến giống như một bí danh cho một đối tượng dưới bề mặt
Hãy xem việc gán cho một biến thực sự có ý nghĩa như thế nào trong Python và cách nó liên quan đến danh tính của các đối tượng
Tham chiếu đối tượng Python
Hãy xem đoạn mã này
>>> print[1000] 1000
Khi bạn chạy nó, trình thông dịch Python
- Tạo một đối tượng số nguyên
- Gán giá trị 1000 cho nó
- Hiển thị giá trị 1000 trong bảng điều khiển
Nhưng sau này, bạn không còn cách nào để truy cập đối tượng số nguyên đó nữa. Nó trở nên mồ côi. Tuy nhiên, bạn có thể “lưu” đối tượng này vào một biến
Nhưng tại sao từ "cửa hàng" trong dấu ngoặc kép?
Trong thực tế, bạn không thể thực sự lưu trữ các đối tượng thành các biến trong Python. Thay vào đó, mỗi biến hoạt động như một tham chiếu đến địa chỉ bộ nhớ thực nơi đối tượng tồn tại
Để chứng minh điều này, hãy tạo một biến lưu trữ một số nguyên
>>> num = 1000
Đoạn mã này hoạt động sao cho nó
- Tạo một đối tượng số nguyên
- Gán cho đối tượng một giá trị 1000
- Tạo bí danh gọi là num có thể được sử dụng để chỉ đối tượng số nguyên mới
Vì vậy, biến num không lưu trữ đối tượng số nguyên. Nó chỉ trỏ đến địa chỉ bộ nhớ của đối tượng đó
Đây là cách nó trông
Bây giờ, bất cứ khi nào bạn truy cập biến num trong mã của mình, Python sẽ thay thế nó bằng đối tượng int đại diện cho 1000
________số 8Ví dụ. Hãy tạo hai biến danh sách sao cho biến thứ hai được đặt bằng biến thứ nhất
>>> a = 1000 >>> id[a] 1400532303239520
Sau đó, hãy thay đổi số đầu tiên của danh sách a thành 1000 và kiểm tra nội dung của danh sách
>>> a = 1000 >>> id[a] 1400532303239521
Đợi tí. Thay đổi giá trị của danh sách a cũng làm thay đổi giá trị của danh sách b. Lý do tại sao điều này xảy ra?
Như bạn đã học, một biến là một con trỏ tới vị trí bộ nhớ nơi đối tượng thực sự tồn tại. Trong ví dụ trên, trước tiên bạn tạo một biến a trỏ đến một danh sách
>>> a = 1000 >>> id[a] 1400532303239522
Sau đó, bạn tạo một biến b mới trỏ đến biến a
>>> a = 1000 >>> id[a] 1400532303239523
Như bạn đã biết, khi bạn gọi một biến, bạn sẽ nhận được đối tượng mà biến hướng tới. Vì vậy, biến mới b trở thành bí danh cho đối tượng được gọi bởi a
Nói cách khác, bây giờ cả a và b đều trỏ đến cùng một đối tượng phía sau cùng một địa chỉ bộ nhớ. Vì vậy, nếu bạn sửa đổi danh sách, cả a và b sẽ thay đổi
Bạn có thể xác minh rằng các đối tượng trỏ đến cùng một địa chỉ bộ nhớ bằng cách sử dụng câu lệnh is
>>> a = 1000 >>> id[a] 1400532303239524
Bây giờ bạn đã hiểu rằng các biến Python chỉ là tham chiếu đến các đối tượng thực tế
Để hỗ trợ sự hiểu biết, chúng ta hãy xem một ví dụ khác. Lần này, thay vì xử lý các đối tượng danh sách, hãy tạo một đối tượng số nguyên được tham chiếu bởi các biến a và b
>>> a = 1000 >>> id[a] 1400532303239525
Bây giờ, hãy thay đổi giá trị trong một
>>> a = 1000 >>> id[a] 1400532303239526
Bây giờ, hãy xem các biến a và b trông như thế nào
>>> a = 1000 >>> id[a] 1400532303239527
Họ khác nhau. Các biến a và b trỏ đến cùng một vị trí bộ nhớ, vậy tại sao b không thay đổi khi a thay đổi?
Lý do tại sao điều này xảy ra là bạn thực sự không cập nhật đối tượng số nguyên ban đầu. Thay vào đó, bạn đang tạo một đối tượng số nguyên hoàn toàn mới mà bạn gán cho biến a
Như bạn còn nhớ, phép gán biến
>>> a = 1000 >>> id[a] 1400532303239526
Báo cho trình thông dịch Python biết
- Tạo một đối tượng số nguyên mới vào một địa chỉ bộ nhớ mới
- Đặt cho nó một giá trị 2000
- Cho phép gọi đối tượng với tên a
Nói cách khác, gán 2000 cho biến a làm cho nó trỏ đến một đối tượng số nguyên mới nằm ở nơi khác trong bộ nhớ. Mặt khác, biến b vẫn trỏ đến đối tượng mà trước đó biến a đã trỏ đến
Bạn có thể xác minh rằng các biến trỏ đến các đối tượng khác nhau bằng cách sử dụng câu lệnh is
>>> a = 1000 >>> id[a] 1400532303239529
Nhân tiện, một số nguyên là một đối tượng bất biến trong Python. Ví dụ này chứng minh điều đó tốt. Không có cách nào để sửa đổi một đối tượng số nguyên hiện có. Thay vào đó, bạn luôn tạo một đối tượng mới để “thay đổi” giá trị của đối tượng ban đầu
Ngoại lệ nhận dạng
Tại thời điểm này, bạn biết rằng phép gán biến trong Python tạo tham chiếu đến một đối tượng
Với điều này trong tâm trí, bạn không ngạc nhiên bởi
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False0
Ở đây các biến a và b đề cập đến các đối tượng khác nhau trong bộ nhớ
Nhưng điều có thể gây ngạc nhiên là lặp lại thí nghiệm này với các giá trị nhỏ hơn thì các đặc tính giống nhau
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False1
Vậy tại sao điều này lại xảy ra?
Khi bạn chạy một chương trình Python, trình thông dịch Python sẽ thực hiện một số tối ưu hóa bên trong. Một trong những cách tối ưu hóa là nó tạo ra các đối tượng đại diện cho các số nguyên từ -5 đến 256. Điều này đơn giản là vì những giá trị số nguyên đó được sử dụng phổ biến
Bây giờ, nếu bạn khởi tạo một số nguyên có giá trị nằm trong phạm vi này, trình thông dịch Python sẽ sử dụng lại một đối tượng số nguyên dựng sẵn tương ứng thay vì tạo một đối tượng mới. Vì vậy, một biến giữa -5 và 256 luôn tham chiếu đến cùng một đối tượng số nguyên dựng sẵn
Nếu bạn tạo một số nguyên nằm ngoài phạm vi [-5, 256], bạn luôn tạo một đối tượng số nguyên mới
Điều này dẫn đến sự không thống nhất khi sử dụng câu lệnh is over ==
Ví dụ
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False2
Ở đây a và b đề cập đến cùng một địa chỉ trong bộ nhớ do tối ưu hóa được mô tả ở trên. Mặt khác, các giá trị x và y không được tối ưu hóa và do đó trỏ đến các địa chỉ bộ nhớ khác nhau
Để hiểu rõ hơn, đừng bao giờ sử dụng câu lệnh is để so sánh hai biến có giá trị bằng nhau
Khi Sử Dụng “==” Và Khi Sử Dụng “is”
Hầu hết thời gian, bạn nên sử dụng == khi so sánh trong Python
Một nguyên tắc cơ bản của ngón tay cái là
- Sử dụng == để kiểm tra xem hai đối tượng có cùng giá trị không
- Sử dụng câu lệnh is để kiểm tra xem hai biến có tham chiếu đến cùng một đối tượng không
Hãy xem một số ví dụ
Ví dụ về giá trị bằng nhau
Khi bạn so sánh các số nguyên, chuỗi, danh sách, tập hợp, từ điển hoặc các đối tượng có thể thay đổi tùy chỉnh khác, hãy sử dụng toán tử đẳng thức ==
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False3
Không Ví dụ
Cách tốt nhất là nếu bạn so sánh thứ gì đó với Không, hãy sử dụng câu lệnh is. Không sử dụng toán tử đẳng thức ==
Ví dụ
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False4
Điều này cũng được đề xuất bởi PEP8, hướng dẫn phong cách chính thức cho Python
Việc so sánh với các đơn vị như
id[obj1] == id[obj2]1 phải luôn được thực hiện vớiid[obj1] == id[obj2]2 hoặcid[obj1] == id[obj2]3, không bao giờ là toán tử đẳng thứcPEP8
Điều này là do có thể viết các phương thức vào các lớp tùy chỉnh xử lý == Không khác gì bạn mong đợi
Ví dụ
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False5
Như bạn có thể thấy, so sánh mảng các số 0 với Không bằng cách sử dụng toán tử đẳng thức sẽ cho bạn một mảng Booleans. Tuy nhiên, so sánh mảng với Không bằng cách sử dụng câu lệnh is sẽ cho bạn kết quả như mong đợi
Ví dụ sơ thẩm lớp
Thông thường, sử dụng câu lệnh is hữu ích khi bạn muốn so sánh một đối tượng với một thứ chỉ tồn tại một lần trong bộ nhớ
Ví dụ: so sánh các thể hiện của lớp có thể thông minh hơn bằng cách sử dụng câu lệnh is. Điều này là do bạn có thể muốn đảm bảo rằng mỗi thể hiện của lớp là duy nhất trong chương trình
Nhưng tại sao không sử dụng toán tử == trong trường hợp đó?
Bởi vì bạn có thể ghi đè hành vi của toán tử == trên các đối tượng tùy chỉnh
Ví dụ: giả sử bạn có lớp Người dùng trong đó bạn có thể so sánh người dùng theo tên của họ. Nếu tên của hai người dùng giống nhau, toán tử == trả về True. Để làm điều này, bạn cần ghi đè lên một phương thức đặc biệt gọi là __eq__[] để xác định điều gì sẽ xảy ra khi gọi == giữa hai đối tượng
Đây là mã
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False6
Bây giờ bạn có thể kiểm tra xem hai người dùng có cùng tên hay không bằng cách sử dụng toán tử đẳng thức
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False7
đầu ra
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False8
Tuy nhiên, bây giờ không thể kiểm tra xem có hai biến trỏ đến cùng một đối tượng người dùng hay không. Điều này không tốt vì bạn muốn đảm bảo rằng mỗi người dùng là duy nhất trong chương trình và không có hai người dùng nào tham chiếu đến cùng một đối tượng
Để khắc phục sự cố này, hãy sử dụng để kiểm tra xem người dùng có giống nhau không
>>> n1 = 1000 >>> n2 = 1000 >>> id[n1] 140053229510960 >>> id[n2] 140053229510768 >>> id[n1] == id[n2] False9
đầu ra
id[obj1] == id[obj2]0
Như bạn có thể thấy, mặc dù tên của người dùng giống nhau, nhưng câu lệnh is nhận ra rằng các biến này tham chiếu đến các đối tượng người dùng khác nhau
Để kết luận, trong trường hợp này, sử dụng câu lệnh is tạo ra kết quả đáng tin cậy hơn so với toán tử đẳng thức ==. Sử dụng câu lệnh is bạn có thể chắc chắn nếu chỉ có một người dùng1 trong chương trình
Phần kết luận
Hôm nay bạn đã học được sự khác biệt giữa câu lệnh is và toán tử đẳng thức == trong Python là gì
Tóm lại, câu lệnh is kiểm tra xem hai đối tượng có trỏ đến cùng một đối tượng trong bộ nhớ hay không, nghĩa là nếu chúng có cùng ID
Toán tử đẳng thức == kiểm tra xem hai đối tượng có cùng giá trị không. Nhưng toán tử đẳng thức không quan tâm liệu các đối tượng có thực sự là cùng một đối tượng có cùng ID hay không