Đầu tiên, giả sử rằng chúng ta có vấn đề cần giải quyết như sau: chúng ta muốn class Point2D
mà giá trị trục số luôn luôn là số nguyên.
Plain attribute cho x và y không thể đảm bảo điều này, thay vào đó chúng ta dùng
property với getter, setter.
Chúng ta có đoạn code như ở dưới:
Đoạn code này khá rườm rà và lặp lại, chúng ta có thể chia thành 2 class như ở dưới:
Chúng ta lưu lại với tên OOP08_ex01.py, import và chạy thử, các bạn có thể down file này
ở đây.
Chúng ta thấy là kết quả không đúng, lí do là khi chúng ta gọi p.x = 100.1 thì chúng ta tạo một atrribute
x mới có kiểu float, hoàn toàn không cột được x với class IntegerValue.
Chúng ta đã thấy ở trên, type(p.x) không trả về 'OOP08_ex01.IntegerValue'.
Chúng ta cần bảo Python làm 2 việc:
x = IntegerValue, phải cột biến x vào class này ở run-time.
p.x, sử dụng get và set cho instance của class IntegerValue
Đó là lí do Descriptors xuất hiện, chúng ta có 4 method cho descriptor protocol.
__get__: để lấy giá trị của attribute, p.x
__set__: để set giá trị của attribute, p.x = 100
__delete__: để xóa attribute
__set_name__: mới xuất hiện ở Python 3.6, dùng để lấy property name
Và các descriptor này được chia thành 2 mục chính:
Chúng ta coi ví dụ ở dưới về cách sử dụng __get__:
Chúng ta lưu lại với tên OOP08_ex02.py, import và chạy thử, các bạn có thể down file này
ở đây.
Một ví dụ khác, chẳng hạn chúng ta có một bộ bài, và cần rút 10 lá ngẫu nhiên:
Mình giải thích xíu về tiếng Anh: 'Spade': chuồn, 'Heart': cơ, 'Diamond': rô, 'Club': pích và bộ tứ
này gọi là Suit. Và 2 tới 10 với J, Q, K, A gọi là card.
Chúng ta lưu lại với tên OOP08_ex03.py, import và chạy thử, các bạn có thể down file này
ở đây.
Chúng ta thấy rằng cả 2 property đều làm cùng 1 thứ và chạy song song.
Chúng ta viết lại sử dụng descriptor:
Chúng ta lưu lại với tên OOP08_ex04.py, import và chạy thử, các bạn có thể down file này
ở đây.
Chúng ta thấy rằng kết quả không đổi.