Trong phần trước, tôi đã giới thiệu những vấn đề cơ bản trong Python. Ở phần này, tối sẽ giúp bạn tìm hiểu sâu hơn.
Classes & Objects
Đầu tiên, ta tìm hiểu sơ qua về lý thuyết.
Objects (đối tượng) là các thực thể trong thực tế ta muốn nhắc tới như xe, chó, gà, …. Các object có 2 đặc trưng cơ bản: dữ liệu và hoạt động.
Ví dụ, xe sẽ có các dữ liệu về số bánh xe, số của sổ, số chỗ ngồi,… Chúng là tổ hợp để thực hiện các hoạt động của xe như đi, dừng, nâng lên, hay hiển thị số nhiên liệu còn lại.
Chúng nhận diện các dữ liệu như các thuộc tính và hành động thì như các hàm thực thi trong lập trình hướng đối tượng.
Mỗi Class là một chương trình, trong đó có chứa các object đơn lẻ. Trên thực tế, ta thường tạo ra những object cùng loại với nhau. Ví dụ, xe hơi sẽ có những yếu tố: động cơ, bánh xe, cửa… và mỗi loại xe sẽ được xây dựng trên cùng một bộ chương trình, cùng các thành phần.
Lập trình hướng đối tượng trong Python
Có 02 phần chính trong mỗi chương trình lập trình hướng đối tượng trong Python: class & object.
Mỗi class là một chương trình, một mô hình với các object trong class đó.
Nói chung, class là một mô hình hoặc cách để biểu diễn các thuộc tính và các hàm thực thi. Ví dụ, ta xây dựng một class là Vehicle (phương tiện di chuyển). Với mỗi bộ thuộc tính riêng, ta sẽ xác định được loại xe riêng. Số lượng các bánh xe, loại bồn chứa, sức chứa chỗ ngồi và vận tốc maximum đều là các thuộc tính của 1 phương tiện di chuyển.
Từ đây, ta xây dựng cấu trúc chương trình trên Python một Class Vehicle với các thành phần đơn giản như sau:
class Vehicle: pass
Objects là các instances (trường hợp) của 1 class. Chúng ta tạo 1 instance bằng cách đặt tên cho class.
car = Vehicle() print(car) # <__main__.Vehicle instance at 0x7fb1de6c2638>
Ở đây, car
là 1 object (hoặc instance) của class Vehicle
.
Lưu ý rằng, class Vehicle
có 4 thuộc tính: số lượng bánh xe, loại bồn chứa, sức chứa chỗ ngồi và vận tốc tối đa. Chúng ta sử dụng tất cả những thuộc tính này khi xây dựng một object. Vậy ta sẽ xây dựng hàm xác định cho class như sau:
class Vehicle: def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity): self.number_of_wheels = number_of_wheels self.type_of_tank = type_of_tank self.seating_capacity = seating_capacity self.maximum_velocity = maximum_velocity
Phương thức init được gọi là một constructor. Ở đây khi tạo một object mới, ta sẽ xác định được các thuộc tính của object đó. Hãy tưởng tượng rằng bạn rất yêu thích loại Tesla Model S, và muốn tạo loại object này. Nó sẽ có 4 bánh xe, chạy năng lượng điện, gồm 5 ghế và vận tốc tối đa là 250km/giờ(155 mph). Tạo object như sau:
tesla_model_s = Vehicle(4, 'electric', 5, 250)
Bốn bánh + “loại bồn chứa” bằng điện + 5 ghế + vận tốc tối đa 250km/ giờ.
Tất cả những thuộc tính đều được đưa vào như vậy. Nhưng làm thế nào ta có thể truy cập vào giá trị những thuộc tính này? Ta sẽ xây dựng một hàm để gọi giá trị ra.
class Vehicle: def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity): self.number_of_wheels = number_of_wheels self.type_of_tank = type_of_tank self.seating_capacity = seating_capacity self.maximum_velocity = maximum_velocity def number_of_wheels(self): return self.number_of_wheels def set_number_of_wheels(self, number): self.number_of_wheels = number
Chúng ta tạo 02 hàm, đó là number_of_wheels và set_number_of_wheels. Chúng ta gọi nó là getter
& setter
. getter
lấy giá trị thuộc tính và setter
thiết lập 1 giá trị mới cho thuộc tính đó.
Trong Python, thì sử dụng @property
(decorators
) để xác định getters
và setters
.
class Vehicle: def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity): self.number_of_wheels = number_of_wheels self.type_of_tank = type_of_tank self.seating_capacity = seating_capacity self.maximum_velocity = maximum_velocity @property def number_of_wheels(self): return self.number_of_wheels @number_of_wheels.setter def number_of_wheels(self, number): self.number_of_wheels = number
Và sử dụng các hàm này như những thuộc tính:
tesla_model_s = Vehicle(4, 'electric', 5, 250) print(tesla_model_s.number_of_wheels) # 4 tesla_model_s.number_of_wheels = 2 # setting number of wheels to 2 print(tesla_model_s.number_of_wheels) # 2
Điều này hơi khác với hàm xác định. Các hàm hoạt động như một thuộc tính. Ví dụ, khi set số bánh xe mới, chúng ta không áp số 2 vào thành 1 tham số mà set giá trị 2 vào number_of_wheels
. Đây là 1 cách để viết code pythonic
getter
và setter
.
Ngoài ra, ta còn có thể sử dụng hàm cho những mục đích khác. Chẳng hạn, hàm make_noise:
class Vehicle: def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity): self.number_of_wheels = number_of_wheels self.type_of_tank = type_of_tank self.seating_capacity = seating_capacity self.maximum_velocity = maximum_velocity def make_noise(self): print('VRUUUUUUUM')
Khi chúng ta gọi hàm ra, biểu thức kết quả sẽ hiển thị: “VRRRRUUUUM.”
tesla_model_s = Vehicle(4, 'electric', 5, 250) tesla_model_s.make_noise() # VRUUUUUUUM
Encapsulation: Ẩn thông tin
Encapsulation (đóng gói) là một cơ chế nhằm hạn chế quyền truy cập vào trạng thái bên trong đối tượng.Nhưng đồng thời, nó hỗ trợ thực hiện trên data đó (cả object và hàm thực hiện).
“Encapsulation can be used to hide data members and members function. Under this definition, encapsulation means that the internal representation of an object is generally hidden from view outside of the object’s definition.” — Wikipedia
Điều này ngăn việc dữ liệu bị sửa đổi trực tiếp. Tất cả hiển thị nội bộ của object sẽ được ẩn đi. Chỉ có object đó mới tương tác được với data nội bộ của nó.
Đầu tiên, ta cùng tìm hiểu value và method của 2 trường hợp public
và non-public
.
Value trong trường hợp Public
Với một Class trong Python, chúng ta có thể tạo ra một public instance variable bằng một constructor.
class Person: def __init__(self, first_name): self.first_name = first_name
Ở đây, áp dụng value first_name
như 1 đối số đến public instance variable
.
tk = Person('TK') print(tk.first_name) # => TK
Nhưng với Class:
class Person: first_name = 'TK'
Ta không cần áp dụng first_name
như 1 đối số, và tất cả các objects sẽ có 1 class attribute
được khởi tạo với TK
.
tk = Person() print(tk.first_name) # => TK
Hiện chúng ta đã biết sử dụng public instance variables
và thuộc tính class
. Điều thú vị khác nữa về phần public
là có thể quản lý giá trị của biến, nghĩa là object
có thể quản lý giá trị biến số của nó: các giá trị biến số Get
và Set
.
Nhắc đến Person
, nếu muốn set giá trị khác cho biến first_name
:
tk = Person('TK') tk.first_name = 'Kaio' print(tk.first_name) # => Kaio
Chúng ta vừa gắn giá trị mới cho trường first_name và cập nhật lại giá trị. Vì đây là Public nên ta có thể làm được điều đó.
Value trong trường hợp Non-Public
We don’t use the term “private” here, since no attribute is really private in Python (without a generally unnecessary amount of work). — PEP 8
Giống như trường hợp trên: public instance variable
, chúng ta có thể xác địnhnon-public instance variable
trong hàm constructor hoặc trong class. Điểm khác biệt trong cú pháp chính là: đối với non-public instance variables
, sẽ sử dụng gạch dưới (_
) trước tên variable
.
“‘Private’ instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam
) should be treated as a non-public part of the API (whether it is a function, a method or a data member)” — Python Software Foundation
Ví dụ:
class Person: def __init__(self, first_name, email): self.first_name = first_name self._email = email
Bạn có thấy biến email
không? Đây là cách chúng ta xác định 1 non-public variable
tk = Person('TK', 'tk@mail.com') print(tk._email) # tk@mail.com
“Chúng ta có thể access & cập nhật nó. Non-public variables
chỉ là 1 Quy ước và nên được xem như phần non-public của API”
Vì vậy, sử dụng một hàm cho phép xác định trong class. Ta cùng thực hiện hai hàm (email
và update_email
) để hiểu rõ hơn:
class Person: def __init__(self, first_name, email): self.first_name = first_name self._email = email def update_email(self, new_email): self._email = new_email def email(self): return self._email
Bây giờ chúng ta có thể cập nhập & truy cập non-public variables
bằng những methods đó. Cùng xem:
tk = Person('TK', 'tk@mail.com') print(tk.email()) # => tk@mail.com tk._email = 'new_tk@mail.com' print(tk.email()) # => tk@mail.com tk.update_email('new_tk@mail.com') print(tk.email()) # => new_tk@mail.com
- Khởi tạo 1 object mới bằng
first_name
TK vàemail
tk@mail.com - Printt email bằng cách tiếp cận
non-public variable
với 1 method - Cố gắng set
email
mới ngoài class - Cần phải xem
non-public variable
như phầnnon-public
của API - Cập nhật
non-public variable
bằng method instance - Chúng ta có thể cập nhật nó trong class bằng method helper
Public Method
Với public methods
, chúng ta cũng có thể sử dụng ngoài class:
class Person: def __init__(self, first_name, age): self.first_name = first_name self._age = age def show_age(self): return self._age
Kiểm tra lại:
tk = Person('TK', 25) print(tk.show_age()) # => 25
Thật tuyệt, vậy là không có vấn đề gì.
Non-public Method
Nhưng với non-public methods
thì không làm được điều này. Hãy biểu diễn cùng class Person
nhưng bây giờ với 1 show_age
non-public method
bằng 1 dấu gạch dưới (_
).
class Person: def __init__(self, first_name, age): self.first_name = first_name self._age = age def _show_age(self): return self._age
Giờ chúng ta sẽ thử gọi non-public method
này với object của mình:
tk = Person('TK', 25) print(tk._show_age()) # => 25
“Chúng ta có thể access & Update nó. Non-public methods
chỉ là 1 quy ước & nên được xem như 1 phần non-public của API”
Dưới đây là ví dụ về cách sử dụng non-public methods:
class Person: def __init__(self, first_name, age): self.first_name = first_name self._age = age def show_age(self): return self._get_age() def _get_age(self): return self._age tk = Person('TK', 25) print(tk.show_age()) # => 25
Chúng ta có 1 _get_agenon-public method
và 1 show_agepublic method
. Object của chúng ta có thể sử dụng show_age
(bên ngoài class) và _get_age
chỉ được sử dụng bên trong class definition (trong method show_age
). Nhưng 1 lần nữa, đây chỉ là vấn đề liên quan đến quy ước.
Kết Encapsulation
Với encapsulation, có thể đảm bảo rằng hiển thị nội bộ của object được ẩn.
Inheritance: các hành vi và các đặc tính
Một vài objects nào đó sẽ sở hữu vài điểm chung: behavior & characterists của chúng.
Trong lập trình hướng đối tượng, các class có thể thừa hưởng những đặc tính (data) và hành vi (methods) tương tự từ class khác.
Cùng xem 1 ví dụ khác và implement nó bằng Python.
Hãy tưởng tượng 1 chiếc xe hơi. Số lượng bánh xe, sức chứa chỗ ngồi và vận tốc tối đa là tất cả các attributes của 1 chiếc xe. Có thể nói rằng 1 classElectricCar thừa hưởng cùng các attributes từ class Car thông dụng.
Class Car đã implement như sau:
class Car: def __init__(self, number_of_wheels, seating_capacity, maximum_velocity): self.number_of_wheels = number_of_wheels self.seating_capacity = seating_capacity self.maximum_velocity = maximum_velocity
Một khi đã khởi tạo, chúng ta có thể sử dụng tất cả instance variables
đã được tạo ra. Tốt.
Trong Python, hãy áp dụng parent class
vào child class
như 1 tham số. Một class ElectricCar có thể kế thừa từ class Car.
class ElectricCar(Car): def __init__(self, number_of_wheels, seating_capacity, maximum_velocity): Car.__init__(self, number_of_wheels, seating_capacity, maximum_velocity)
Đơn giản vậy thôi, chúng ta không cần phải implement bất kì method nào khác, vì class nà đã có rồi (được thừa hưởng từ class Car).
my_electric_car = ElectricCar(4, 5, 250) print(my_electric_car.number_of_wheels) # => 4 print(my_electric_car.seating_capacity) # => 5 print(my_electric_car.maximum_velocity) # => 250
Tổng quan
Như vậy, bài viết này đã giúp chúng ta nắm được những kiến thức Python cơ bản:
- Cách hoạt động của các biến Python
- Cách hoạt động của conditional statements Python
- Cách looping Python (while & for) hoạt động
- Cách sử dụng Lists: Collection | Array
- Dictionary Key-Value Collection
- Cách lặp thông qua data structures
- Objects & Classes
- Các attibutes như data của objects
- Methods như hành vi của các objects
- Sử dụng getters và setters của Python & property decorator
- Encapsulation: ẩn thông tin
- Inheritance: behaviors (hành vi) và characteristics (đặc tính)
Nếu bạn muốn có thêm thông tin, bạn có thể tham khảo One Month Python Bootcamp.
Hi vọng bài viết có thể giúp ích cho bạn.
Hãy theo dõi https://trituenhantao.io/ để có thêm nhiều thông tin mới nhé. Ngoài ra, các bạn có thể đăng ký nhận ebook về python miễn phí tại đây.