Lập trình Python cơ bản

Lập trình Python cơ bản

Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python 5/5 (113 reviews)

Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python

Đã đăng 2017-11-03 13:41:49 bởi Kteam
9 bình luận 8634 lượt xem
Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python 5 /5 stars (2 reviews)
 

Dẫn nhập

Trong các bài trước, Kteam đã giới thiệu đến bạn KIỂU DỮ LIỆU TUPLE, một container thuộc thể loại hashable object trong Python

Ở bài này Kteam sẽ nói về sự khác nhau của toán tử giữa hai loại kiểu dữ liệu Hashable Object (immutable) và Unhashable Object (mutable) trong Python.


Nội dung

Để đọc hiểu bài này tốt nhất bạn cần:

Bạn và Kteam sẽ cùng tìm hiểu những nội dung sau đây

  • Giới thiệu cơ bản về hàm id
  • Toán tử là một phương thức
  • Khác biệt về toán tử Hash Object và Unhash Object
  • Tại sao có List lại còn sinh ra Tuple? Hoặc là sử dụng Tuple thôi, cần gì tới List?

Giới thiệu cơ bản về hàm id

Cú pháp:

id(<giá trị>)

Như Kteam đã từng đề cập ở các bài trước đây, mọi thứ trong Python xoay quanh các đối tượng, và các giá trị ở đây chính là một đối tượng. Tuy vậy vẫn để là <giá trị> để tránh gây khó hiểu.

Công dụng: Theo định nghĩa về hàm id trong tài liệu của Python thì hàm này sẽ trả về một số nguyên (int hoặc longint).

  • Giá trị này là một giá trị duy nhất và là hằng số không thay đổi suốt chương trình.
  • Trong chi tiết bổ sung của CPython có nói giá trị trả về của hàm id là địa chỉ của giá trị (đối tượng) đó trong bộ nhớ.

Cao siêu là thế, nhưng bạn hoàn toàn có thể nghĩ đơn giản, con số trả về đó như cái số nhà của bạn. Bạn ở đâu, thì số nhà của bạn cũng sẽ tương ứng.

>>> n = 69
>>> s = 'How KTeam'
>>> lst = [1, 2]
>>> tup = (3, 4)
>>> id(n)
1446271792
>>> id(s)
53865712
>>> id(lst)
53838352
>>> id(tup)
53865768
>>>
>>> id(123)
1446272656
>>> id('Free Education')
53865832

Kteam sẽ tiếp tục giới thiệu hàm id khi nói tới các toán tử so sánh trong Python ở một bài khác.


Toán tử là một phương thức

Lặp lại thêm một lần nữa, mọi thứ xoay quanh Python toàn là hướng đối tượng. Cả các toán tử cũng thế!

>>> n = 69
>>> n + 1
70
>>> n
69
>>> n.__add__(1) # tương tự khi bạn n + 1
70
>>> n
69	
>>> n.__sub__(9) # tương tự n - 9
60
>>> n.__mul__(2) # tương tự n * 2
138
>>> n.__radd__(1) # tương tự 1 + n
70
>>> n.__rsub__(9) # tương tự 9 - n
-60
>>> n.__neg__() # tương tự -n
-69

Mỗi toán tử của mỗi đối tượng sẽ có toán tử đi kèm.


Khác biệt về toán tử Hash Object và Unhash Object

Vấn đề chính của bài này, là chỉ ra sự khác biệt giữa toán tử ở hash objectunhash object. Kteam sẽ lấy ví dụ so sánh đơn giản đó chính là sự khác biệt giữa việc s = s + i với lại s += i

Hãy xem xét đoạn code dưới đây, Kteam sẽ xét một hash object là chuỗi:

>>> s_1 = 'HowKteam'
>>> s_2 = 'Free Education'
>>> id(s_1)
53866032
>>> id(s_2)
53865712
>>> s_1 = s_1 + ' Python'
>>> s_2 += ' Python'
>>> id(s_1) # đã có sự thay đổi
53866152
>>> id(s_2) # cũng có sự thay đổi
23088304
>>> s_1
'HowKteam Python'
>>> s_2
'Free Education Python'

Ta cũng thấy, 2 toán tử = + cũng không có gì khác biệt lắm so với +=.

Giờ ta xét tới một unhash object

>>> lst_1 = [1, 2]
>>> lst_2 = [3, 4]
>>> id(lst_1)
53839752
>>> id(lst_2)
53864048
>>> lst_1 = lst_1 + [0]
>>> lst_2 += [0]
>>> id(lst_1) # có sự thay đổi
53864088
>>> id(lst_2) # không hề có sự thay đổi
53864048
>>> lst_1
[1, 2, 0]
>>> lst_2
[3, 4, 0]

Đã có khác biệt, khi thử với unhash object. Tại sao lại như vậy?

Đó là vì khi bạn làm như cách dưới đây. Tức có nghĩa bạn vừa mới gán lại giá trị cho biến lst. Nói cách khác, bạn đã đưa lst tới một địa chỉ khác.

>>> lst = [1, 2]
>>> lst = lst + [3]
>>> lst
[1, 2, 3]

Còn khi bạn làm như thế này

>>> lst = [1, 2]
>>> lst += [3]
>>> lst
[1, 2, 3]

Thì không như vậy, bạn đã gián tiếp gọi một phương thức

>>> lst = [1, 2]
>>> id(lst)
53839752
>>> lst.__iadd__([3])
[1, 2, 3]
>>> id(lst)
53839752
>>> lst
[1, 2, 3]

Vậy vì sao, các hash object lại không như vậy?

Là bởi vì các hash object không hề có phương thức iadd, hay imul như các unhash object. Thế nên, khi bạn dùng toán tử +=, Python sẽ làm tương tự như bạn dùng cách gán giá trị.

Vì sao các hash object lại không có phương thức iadd, imul?

Khi bạn khởi tạo một giá trị, nó sẽ được lưu trong bộ nhớ máy tính.

  • Với hash object, bạn không thể thay đổi nội dung của nó. Do đó, Python sẽ xin đủ khoảng trống để lưu trữ dữ liệu của bạn, không nhiều hơn và cũng không ít hơn. Giúp không hoang phí bộ nhớ của bạn. Thế nên, khi bạn cộng thêm một thứ gì đó, Python không biết nhét cái thứ bạn muốn cộng vào chỗ nào. Nên nó đành cuốn gói đi ra chỗ đó, tìm chỗ mới thoáng có đủ khoảng trống.
  • Còn với unhash object. Là một đối tượng bạn thay đổi được nội dung, vì thế, Python luôn xin dư bộ nhớ để chừa chỗ cho các giá trị tiếp theo bạn có thể thêm vào. Trong bài trước, Kteam đã đề cập đến việc Tuple chiếm ít dung lượng hơn List vì Tuple là hash object. (bạn có thể tham khảo chi tiết tại bài KIỂU DỮ LIỆU TUPLE

Tại sao có List lại còn sinh ra Tuple? Hoặc là sử dụng Tuple thôi, cần gì tới List?

Đáng lẽ, Kteam sẽ nói vấn đề này ở bài trước, nhưng vì muốn bản hiểu hơn về các hash object với unhash object nên đã để tới bài này.

Bạn dễ dàng nhận thấy, việc ta thay đổi giá trị của Tuple, không nhất thiết là phải trực tiếp như List.

>>> lst = [1, 2]
>>> lst.append(3)
>>> lst
[1, 2, 3]
>>> tup = (1, 2)
>>> tup += (3,)
>>> tup
(1, 2, 3)

Các bạn cũng thấy, nó không khác nhau là mấy. Ta cũng có thể tạo ra các hàm thay đổi nội dung của Tuple bằng cách slicing. Đã thế List lại còn nặng về việc chiếm nhiều dung lương hơn Tuple, truy xuất chậm hơn Tuple. Việc gì khiến nó còn được trọng dụng?

Vì khi bạn thay đổi Tuple như cách trên, Python phải đi vòng vòng trong bộ nhớ của bạn tìm xem chỗ nào trống, phù hợp để chứa cái Tuple của bạn không, trong khi với List thì không. Do đó, bạn phải biết được dữ liệu của bạn là dạng dữ liệu như thế nào, có cần phải thay đổi không. Dựa vào đó, để chọn ra một kiểu dữ liệu phù hợp cho mình, tối ưu hóa dung lượng sử dụng, thời gian truy xuất.


Củng cố bài học

Đáp án bài trước

Bạn có thể tìm thấy câu hỏi của phần này tại CÂU HỎI CỦNG CỐ trong bài KIỂU DỮ LIỆU TUPLE TRONG PYTHON.

  1. Chỉ có d là cách khởi tạo đúng. Bạn sẽ hiểu được khái niệm này khi biết tới Unpacking Packing argument sẽ được Kteam giới thiệu trong tương lai.
  2. c đúng

Nếu bạn thắc mắc vì sao có lỗi. Trong khi ví dụ ở phần “Có phải Tuple luôn là một hash object?” thì lại không có lỗi?

Lí là do vì trong ví dụ phần “Có phải Tuple luôn là một hash object?”. Việc thay đổi nội dung List trong Tuple như thế thì Python chỉ làm việc duy nhất với List đó. Không liên quan gì đến Tuple chứa nó.

Riêng ở câu hỏi này, Python đã làm như thế này

  • Đưa phần tử tup[2] lên TOS (Top of stack)
  • Gán TOS (chính là List [3, 4]) bằng việc cộng thêm cho List đó một List nữa là [50, 60]. Suy ra, List bây giờ đang là [3, 4, 50, 60]
  • Sau đó, Python gán lại tup[2] = TOS. Và dĩ nhiên bạn cũng biết, Tuple không thể làm như vậy.

Có thể bạn chưa nắm được kiến thức này, nhưng bạn sẽ thấy nó không hề khó khi đã theo dõi phần hàm id ở đầu bài này.


Kết luận

Bài viết này đã cho bạn biết được cách hoạt động của các toán tử trong Python và một vài sự khác biệt

Ở bài sau, Kteam sẽ nói về một kiểu dữ liệu nữa, đó chính là KIỂU DỮ LIỆU SET trong Python

Cảm ơn bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.

 



Tài liệu 

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học SỰ KHÁC NHAU VỀ TOÁN TỬ CỦA HASHABLE OBJECT & UNHASHABLE OBJECT TRONG PYTHON dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên like hoặc +1 Google để ủng hộ Kteam và tác giả nhé! 


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần BÌNH LUẬN bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng. 

 

Chia sẻ:
Thảo luận Hỏi và đáp Báo lỗi bài viết
Hủy bỏ   hoặc  
Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python
alforever 2018-08-11 10:57:38

a = id(2)
print(a)

93969827296640

94726396794240

94093688685952

=> Không biết có ai giống em không? em chạy mỗi lần đều là mỗi kết quả khác  nhau

0 bình chọn
Reply
View all 1 comments
Kteam - Howkteam Free Education
alforever 2018-08-11 11:03:02
Sau khi kiểm tra em nhận thấy: khi chạy bằng Terminal thì giá trị không đổi, còn chạy bằng Sublime text thì giá trị luôn đổi như trên =&gt; mong Kteam giải thích giúp em nguyên nhân để có thể hiểu chính xác hơn. Em cảm ơn!
0 bình chọn
Reply
Sự khác nhau về toán tử của Hashable object và Unhashable object trong Python
Lưu Tuấn Cường 2017-11-18 16:27:27

em xin góp ý:

hình như trong video(đoạn 20:00) kteam nói :'+=' = __add__()

em đã thử và thấy điều đó không đúng, em đã phát hiện qua đoạn code ngắn:

a = [1,2]                 |
print(id(a))              | =>>in ra id của biến 'a=[1,2]'
a.__add__([3,4])    |=>> toán tử này không thực hiện cộng list [3.4] vào biến a
print(id(a))              |=>>thực ra cũng in ra id của biến 'a=[1,2]' chứ không phải id của biến a sau khi cộng list 'a=[1,2,3,4]
print(a)                   |=>>in ra biến 'a=[1,2]' chứ không biến a sau khi cộng list 'a=[1,2,,3,4]' 

kết quả: 

90051408    | =>> id của biến 'a=[1,2]
90051408    | =>> id củng của biến 'a=[1,2]' chứ không phải của biến '[a=1,2,3,4]'
[1, 2]

em xin hết, mong góp ý này :

1 là e hiểu ra cái em không hiểu rõ mà lên đây góp ý nhầm

2 là ngược lại, góp ý cho kteam chinh sửa bài viết  chính xác hơn cho các bạn học free như em

0 bình chọn
Reply
View all 6 comments
Kteam - Howkteam Free Education
Lưu Tuấn Cường 2017-11-18 17:20:22
nhưng a = a.__add__([3,4]) vẫn cộng list bình thường ạ
0 bình chọn
Reply
Kteam - Howkteam Free Education
nguyenhuuduychanh 2018-03-04 16:46:03
cái khúc đó thì sai thật nhưng list là unhash thì kết quả id = 90051408 thì đúng
0 bình chọn
Reply
Kteam - Howkteam Free Education
Kteam 2018-03-18 11:09:30
Trong video có chút nhầm lẫn, Kteam chưa có thời gian quay lại. Còn bài viết chính xác nên bạn dựa theo bài viết nhé!
0 bình chọn
Reply
Kteam - Howkteam Free Education
Kteam 2018-03-19 15:22:01
Cảm ơn bạn đã cùng team cải thiện bài viết! :)
0 bình chọn
Reply
Kteam - Howkteam Free Education
duy van 2018-06-24 20:07:02
mình nghĩ là a.__add__([3,4]) giống với a&#43;[3,4] nhưng chưa được gán cho biến cho nên khi print(a) thì chỉ hiện a trước đó @@
0 bình chọn
Reply
Kteam - Howkteam Free Education
tuananh 2018-10-21 19:16:38
mình tưởng là toán tử gán &#43;= là dành cho các unhash còn các iadd,imul kiể __add__ là dành cho các hash chứ?,còn việc khi xuất ra mà hình thì là vì kiểu dữ liệu list nên id nó sẽ k đổi vì xin dư bộ nhơ, mình mới học qua bài và cách đây 11 tháng k biết còn dsdung hay sai :v
0 bình chọn
Reply
Hủy bỏ   hoặc  
Hủy bỏ   hoặc  

Chiến dịch

Kteam - Howkteam Free Education