Nội dung phần này:
- Khởi tạo Instance của một class
- Tạo Attribute tại run-time
- Property
- Property Decorator
- Read-Only and Computed Properties
- Deleting Properties
- Static method
- Một vài kiểu dữ liệu của Python
- Scope
Danh sách đầy đủ bài học ở đây.
07. Khởi tạo Class Instance
Khi chúng ta khởi tạo một class, thì Python mặc định sẽ làm 2 thứ:
- Tạo một instance của class
- Khởi tạo namespace cho class
Tuy nhiên, chúng ta có thể thay đổi điều này, không cần xài mặc định:
Khi chúng ta gọi MyClass(‘3.7’), Python sẽ tạo một instance với namespace hoàn toàn rỗng.
Nếu chúng ta định nghĩa hàm __init__
trong class:
- Class sẽ gọi
obj.__init__('3.7')
, tương đương vớiMyClass.__init__(obj, '3.7')
. - Hàm này sẽ chạy và thêm
version
vào namespace của obj. version
sẽ là instance, vàobj.__dict__
lúc này sẽ là {‘version’: ‘3.7’}.- Qui ước dùng
obj
ở trên bằng một object tên là self.
Lưu ý:
- Khi
__init__
được gọi, thì Python đã tạo xong object và namespace (rỗng) cho nó, sau đó__init__
mới được gọi như là một method bị cột vào instance mới được tạo. - Chúng ta có thể sử dụng một function để tạo object, đó là
__new__
. __init__
không tạo object, nó chỉ chạy sau khi instance đã được tạo.
08. Tạo Attribute tại run-time
Chúng ta coi ví dụ dưới:
Chuyện gì xảy ra khi chúng ta gán như sau: obj.say_hello = lambda: ‘Hello World!’ ?
Nhưng hàm lambda này là function
, không phải bound method
.
Chúng ta có thể tạo method lúc run-time và cột nó vào một instance, dùng MethodType
MethodType(function, object):
09. Property
Chúng ta hiểu property như private, public của một attribute. Ở Python sẽ hơi khác một chút.
Ở ví dụ trên, chúng ta có thể thấy rằng chúng ta truy xuất các Attribute rất dễ dàng, nhưng ở các ngôn ngữ lập trình khác, điều này không được khuyến khích.
Ở các ngôn ngữ này, có qui ước để attribute bí mật (private), tức là bên ngoài không truy xuất được trực tiếp, phải gọi và set thông qua hàm getter
và setter
.
Tuy ở Python, không có private attribute nhưng chúng ta có thể viết như ở dưới:
Chúng ta chỉ có thể truy xuất hoặc gán giá trị mới thông qua 2 method trên:
Nếu bắt đầu chúng ta viết class cho phép truy xuất trực tiếp, sau đó vì lý do gì đó cần chuyển truy xuất gián tiếp qua hàm, chúng ta sẽ phải thay đổi toàn bộ obj.language = ‘Java’ thành obj.set_language(‘Java’).
Python có một giải pháp, đó là sử dụng property
class. Chúng ta xem ví dụ ở dưới:
property
là một class, có các parameter sau:
- fget: input là tên hàm getter
- fset: input là tên hàm setter
- fdel: input là tên hàm deleter
- doc: hiển thị docstring cho property
Lưu ý: trong dictionary của object m vẫn lưu _language không phải language.
10. Property Decorator
property
có thể được khởi tạo bằng nhiều cách khác nhau:
Chúng ta có thể decorate lại như ở dưới:
Hoặc chúng ta có thể viết lại như ở dưới:
Tuy nhiên, ở trên mới chỉ có getter, chúng ta phải thêm setter nữa.
Hoặc chúng ta có thể viết lại như ở dưới:
Lưu ý: với cách viết sau thì tên hàm dưới @property
và dưới @language.setter
phải hoàn toàn giống nhau (ở đây là language).
11. Read-Only and Computed Properties
Để tạo read-only property thì chúng ta phải tạo một attribute sao cho chỉ có getter mà thôi (nhưng thực sự cũng không hoàn toàn read-only).
Chúng ta hãy xem 2 ví dụ dưới:
Lưu ý, ở ví dụ trên chúng ta sử dụng area() như method, còn ở dưới chúng ta chỉ gọi c.area như gọi property của một class.
Ở đây, area được gọi là computed property
, đối với lazy computation
thì area chỉ được tính toán lại khi có yêu cầu, do đó nó sẽ có giá trị trong bộ nhớ đệm gọi là cache value
, nếu ai đó thay đổi radius
thì chúng ta cần phải xóa cache
.
12. Deleting Properties
Để xóa property thì chúng ta có thể sử dụng từ khóa del
hoặc hàm delattr
.
Tuy nhiên, Python cũng hỗ trợ deleter, tượng tự như setter và getter, code ví dụ ở dưới gồm 2 phiên bản:
13. Static method
Chúng ta coi lại một ví dụ cũ:
Chúng ta thấy rằng, MyClass.hello là function
, hay là một method bị cột với MyClass.
Còn m.hello là bound method
, nghĩa là, nó là một method bị cột vào với m, là một instance.
Câu hỏi là: có cách nào làm cho method ở trong class bị cột vào chính class đó mà không bị cột vào với instance nào không?
Câu trả lời là: @classmedthod. Chúng ta hãy xem ví dụ ở dưới:
hello
ở trong MyClass là một regular function, khi tạo instance, nó bị cột vào instance đó, gọi sẽ báo lỗi vì thiếu argument.
inst_hello
ở trong MyClass là cũng là một regular function, khi tạo instance, nó bị cột vào instance đó, gọi sẽ không báo lỗi vì đã có argument.
cls_hello
ở trong MyClass là một method bị cột vào MyClass, kể cả khi tạo instance nó cũng bị cột vào class này.
Ở trên, cls_hello
là một method bị cột vào chính class của mình, chứ không bị cột vào bất kì instance nào.
Câu hỏi là: có thể làm cách nào mà method không bị cột vào bất cứ object nào không, kể cả class của chính nó?
Câu trả lời là: @staticmedthod. Chúng ta hãy xem ví dụ ở dưới:
Chúng ta thấy help() luôn luôn là function
dù ở trong class Circle hay sau khi bị gán với instance c.
Chúng ta xem ví dụ dưới để so sánh:
Ở trên, inst_hello
là một method bị cột vào instance khi instance được gọi.
cls_hello
luôn luôn bị cột vào MyClass, không bị cột vào instance, điều này tương đương với việc cls_hello
luôn nhận MyClass là argument đầu tiên.
static_hello
là một static method, không bị cột vào bất cứ thứ gì, và không cần thêm bất cứ argument nào.
Static method được sử dụng khi có một function nào đó nằm trong class, nhưng chúng ta cần hàm đó không thay đổi dù class bị gán cho bất cứ object nào.
Ví du: Timer
start(self): instance method
end(self): instance method
timezone: class attribute -> cho phép chúng ta thay đổi timezone với mọi instance
current_time_utc(): static method
current_time(cls): class method (needs class time zone)
14. Một vài kiểu dữ liệu của Python
Một vài kiểu dữ liệu chúng ta hay dùng trong Python như int, str, list, tuple… là kiểu dựng sẵn (built-in). Ví dụ:
Nhưng có vài kiểu, không phải là dựng sẵn: function, module, generator….
Chúng ta sử dụng types
module để check:
Ngoài types.FunctionType
còn có types.ModuleType
, types.GeneratorType
… để check kiểu dữ liệu.
15. Scope
Mỗi module, class hay function đều có một phạm vi nhất định, nó là scope
.
Chúng ta xem hình dưới:
Module
: có scope global, chứa class Python lẫn object p.
class
có scope của chính nó, chứa kingdom, phylum , family , __init__, say_hello
Vậy scope của method được định nghĩa trong class thì thế nào? Có nằm trong class đó không?
Thực ra scope của method nằm ngoài class đó, chỉ có cái tên của method (gọi là symbol
) nằm trong class đó thôi.
Chúng ta xem hình dưới để dễ hình dung:
Chúng ta xem ví dụ dưới:
Phần giới thiệu sâu thêm về class và object đến đây là hết. Phần 03 sẽ giới thiệu về Polymorphism (đa hình) và các method đặc biệt.