Python cho phép viết chương trình theo một số mô hình (như lập trình hướng thủ tục, lập trình hàm, lập trình hướng đối tượng).
Trong đó,
Lập trình hướng đối tượng là một trong những mô hình lập trình sử dụng rộng rãi nhất.
Nó được dựa trên các đối tượng — thực thể chứa dữ liệu thành viên gọi là thuộc tính (attributes) và các chức năng gọi là methods.
Đối tượng là một thể hiện (instance) của lớp (class).
Nói cách khác, các class, chủ yếu là xác định được cấu trúc và làm mẫu để tạo ra các đối tượng.
Các class có định nghĩa phương thức nhưng cũng có thể chứa dữ liệu chung cho tất cả các thể hiện của chúng.
Lập trình Hướng đối tượng trong Python
Bài này là bài Hướng dẫn cơ bản về Lập trình hướng đối tượng Python, nó giải thích làm thế nào để tạo ra các class và sử dụng chúng để tạo đối tượng của chúng.
Đặc biệt, bài viết này bao gồm:
Bài viết này không bao gồm tất cả các chi tiết về các chủ đề Lập trình hướng đối tượng.
Nhưng nó có thể cung cấp một nền tảng tốt để bạn bắt đầu học Python và lập trình chương trình theo mô hình hướng đối tượng với Python.
#1. Tạo Class trong Python
Chúng ta định nghĩa một class Python với từ khóa class
, theo sau là tên của class, dấu hai chấm :
và khai triển cụ thể của class:
class MyClass:
pass
Khi bạn tạo một class mà không có khai triển gì, nó sẽ gây ra lỗi.
Để tránh lỗi này thì mình sử dụng từ khóa pass
.
Theo qui ước, tên của class trong Python được đặt theo ThePascalCase.
-
Pascal Case có nghĩa là từ đầu tiên của tất cả các từ phải được viết Hoa, ví dụ:
MyClass
, UserAcount
, AdminAcount
-
Đọc thêm về các kiểu đặt tên tại đây.
Bây giờ,
Hãy thử tạo một thể hiện của class MyClass
chúng ta vừa tạo.
# Tạo class
class MyClass:
pass
# Tạo đối tượng từ class MyClass
a = MyClass()
print(a)
Kết quả:
<__main__.MyClass object at 0x0084F790>
Câu lệnh a = MyClass()
tạo một thể hiện của class MyClass
và gán tham chiếu cho nó cho một biến mới là a
.
Chúng ta có thể lấy được kiểu dữ liệu của MyClass
,
Để làm được việc này Python có một hàm tích hợp (built-in function) là type()
.
Hoặc sử dụng thuộc tính.__class__
.
type(a)
# <class '__main__.MyClass'>
a.__class__
# <class '__main__.MyClass'>
Và thông qua thuộc tính .__class__
, chúng ta cũng có thể lấy được tên của nó.
a.__class__.__name__
# MyClass
Nhân tiện, hãy lưu ý là các class trong Python cũng là các đối tượng, nhưng chúng là thể hiện của class type
.
print (type(MyClass))
# <class 'type'>
Định nghĩa phương thức trong class
Phương thức trong class cũng giống như hàm.
Tuy nhiên, mỗi phương thức trong class đều phải có ít nhất một tham số, tham số đầu tiên trong class tương tự như this
trong các ngôn ngữ khác.
Theo quy ước, trong Python, tham số này được gọi là self
.
Dĩ nhiên, bạn có thể đặt tên bất kỳ, nhưng sử dụng self theo mặc định sẽ dễ hiểu hơn.
Khi gọi phương thức thì không cần truyền đối số cho self
.
Và, một trong những phương thức quan trọng nhất mà chúng ta thường định nghĩa là .__init __()
.
Phương thức này sẽ được gọi sau khi một thể hiện của class được tạo.
Nó khởi tạo các thành viên trong class.
Hãy xem ví dụ để hiểu rõ hơn:
class MyClass:
def __init__(self, arg_1, arg_2, arg_3):
print(f'Một thể hiện của {type(self).__name__} được tạo:')
print(f'arg_1: {arg_1}, arg_2: {arg_2}, arg_3: {arg_3}')
Chúng ta đã tạo một ví dụ về MyClass, để xem những gì sẽ xảy ra.
-
Phương thức
.__init __()
của chúng ta có ba đối số (arg_1, arg_2 và arg_3)
-
Hãy nhớ là chúng ta không cần truyền đối số cho self
Do đó, chúng ta sẽ cung cấp cho nó ba đối số khi chúng tôi khởi tạo đối tượng:
# Khởi tạo đối tượng
# Truyền 3 đối số
a = MyClass(1, 2, 3)
Kết quả:
Một thể hiện của MyClass được tạo:
arg_1: 1, arg_2: 2, arg_3: 3
Đây là những gì vừa xảy ra:
-
Một thể hiện (đối tượng) của
MyClass
vừa được tạo
-
Phương thức .__init__() được gọi tự động
-
3 đối số (
1
, 2
, 3
) chúng ta truyền khi khởi tạo đối tượng được truyền vào .__init__()
-
.__init__()
tiếp tục thực thi 2 lệnh print. Nó lấy tên class bằng type(self).__name__
Bây giờ chúng ta có một class, phương thức .__init__()
của nó và một thể hiện của class này.
Data Attributes
Chúng ta đã có class, bây giờ, hãy tạo cho nó một ít dữ liệu.
Chúng ta khởi tạo và định nghĩa, và cũng có thể thay đổi dữ liệu được gán trong .__init__()
hoặc bất cứ phương thức nào
Ví dụ:
class MyClass:
def __init__(self, x, y, z):
self.x = x
self._y = y
self.__z = z
Bây giờ, chúng ta có ba data attributes:
Bạn cũng có thể viết ngắn gọn hơn như thế này:
class MyClass:
def __init__(self, x, y, z):
self.x, self._y, self.__z = x, y, z
Bạn có để ý dấu gạch dưới _
mình thêm vào trong các biến không?
Mục đích của việc này là thiết lập mức độ hiển thị của biến.
-
Các thuộc tính không có dấu gạch dưới ở đầu (như
.x
) thường có thể được gọi và sửa đổi từ bên ngoài đối tượng.
-
Các thuộc tính có 1 dấu gạch dưới ở đầu (như
._y
) cũng có thể được gọi và sửa đổi từ bên ngoài đối tượng. Tuy nhiên, nó chỉ được gọi và sửa đổi thông qua các các phương thức và thuộc tính của class đó.
-
Các thuộc tính có 2 dấu gạch dưới ở đầu (như
.__z
) sẽ có tên được thay đổi (trong trường hợp này là ._MyClass__z
) trong quy trình được gọi là name mangaling. Chúng cũng có thể được gọi và sửa đổi từ bên ngoài đối tượng với tên mới. Tuy nhiên, nó chỉ được gọi và sửa đổi thông qua các các phương thức, thuộc tính trong class đó
Các data attributes
của các đối tượng Python thường được lưu trữ trong dictionaries có tên là .__dict__
, cũng là thuộc tính của đối tượng.
Tuy nhiên, nó có thể lưu trữ dữ liệu ở những nơi khác. Chúng ta có thể lấy .__dict__
bằng cách gọi trực tiếp hoặc bằng hàm vars()
:
# Khởi tạo đối tượng
a = MyClass(2, 4, 6)
# In dữ liệu
print(vars(a))
print(a.__dict__)
Kết quả:
{'x': 2, '_y': 4, '_MyClass__z': 6}
{'x': 2, '_y': 4, '_MyClass__z': 6}
Key '_MyClass__z'
, ở đó thay vì '__z
bởi vì nó đã trải qua quá trình name magaling.
Chúng ta có thể sử dụng .__dict__
như bất kỳ dictionaries nào khác.
Và đây là cách thông thường để lấy và thay đổi các giá trị được liên kết với các thuộc tính dữ liệu:
print(a.x)
Kết quả:
2
a.x = 10
print(a.x)
Kết quả:
10
print(a._y)
Kết quả:
a._y = 20
print(a._y)
Kết quả:
20
print(a.__z)
Kết quả:
Traceback (most recent call last):
File "C:/Users/Admin/PycharmProjects/Demo2/demo1.py", line 9, in <module>
print(a.__z)
AttributeError: 'MyClass' object has no attribute '__z'
Bởi vì __z
đã được đổi tên (qua quá trình name mangaling), chúng ta không còn có biến __z
nữa, vậy nên truy cập sẽ gây ra lỗi.
print(vars(a))
Kết quả:
{'x': 2, '_y': 4, '_MyClass__z': 6}
Instance Methods
Bây giờ, chúng ta sẽ thử tạo instance method trong class xem thế nào nhé.
-
.get__z()
để trả về giá trị của .__z
Hãy nhớ là tham số đầu tiên của mỗi instance method (được gọi là self
theo quy ước) đề cập đến chính đối tượng đó, nhưng chúng ta không cần cung cấp khi gọi.
class MyClass:
def __init__(self, x, y, z):
self.x, self._y, self.__z = x, y, z
# Sửa đổi giá trị của __z
def set_z(self, value):
self.__z = value
# Lấy giá trị của __z
def get_z(self):
return self.__z
b = MyClass(2, 4, 6)
Các phương thức .get_z()
và .set_z()
cung cấp interface thông thường để truy xuất và sửa đổi giá trị của .__z
:
value_z = b.get_z()
print(value_z)
Kết quả:
6
b.set_z(10)
print(vars(b))
Kết quả:
{'x': 2, '_y': 4, '_MyClass__z': 10}
.get_z()
và .set_z()
có thể mang lại chức năng bổ sung như kiểm tra tính hợp lệ của dữ liệu.
Các phương thức như vậy cho phép chúng ta đạt được tính đóng gói, (encapsulation), đây là một trong những khái niệm chính trong lập trình hướng đối tượng.
Properties
Một cách khác để truy cập vào và thay đổi các data attributes được sử dụng là properties.
Cách làm này để đạt được tính dễ đọc (Còn gọi là Pythonic)
Chúng đóng gói các phương thức getter
, setter
và deleters
— nhưng cư xử như data attributes bình thường.
Đây là cách triển khai thuộc tính .z
có chức năng tương tự như .get_z()
và .set_z()
:
class MyClass:
def __init__(self, x, y, z):
self.x, self._y, self.__z = x, y, z
@property
def z(self):
return self.__z
@z.setter
def z(self, value):
self.__z = value
b = MyClass(2, 4, 8)
Đây là cách chúng ta có thể truy cập và sửa đổi .__z
với thuộc tính .z
tương ứng:
print(b.z)
Kết quả:
8
b.z = 20
print(b.z)
Kết quả:
20
Code như thế này chắc chắn ngắn và dễ đọc hơn đúng không?
Static Method và Static Method
Ngoài các instance method và instance properties, các class có thể có các Class Method và Static Method.
Để hiểu rõ hơn, hãy thêm ba phương thức f
, g
, h
vào MyClass
:
class MyClass:
def __init__(self, x, y, z):
self.x, self._y, self.__z = x, y, z
def f(self, arg):
print('Instance method f được gọi')
print(f'instance: {self}')
print(f'instance attributes:\n{vars(self)}')
print(f'class: {type(self)}')
print(f'arg: {arg}')\
@ classmethod
def g(cls, arg):
print('class method g được gọi')
print(f'cls: {cls}')
print(f'arg: {arg}')
@ staticmethod
def h(arg):
print('static method h được gọi')
print(f'arg: {arg}')
c = MyClass(2, 4, 6)
Phương thức .f()
là một instance method.
Các instance method cũng phải có đối số đầu tiên (self
) đề cập đến chính đối tượng.
Chúng có thể tự truy cập:
-
Vào đối tượng, các thuộc tính dữ liệu của đối tượng bằng
vars(self)
hoặc self.__dict__
-
Vào class tương ứng với đối tượng với
type(self)
hoặc self.__class__
-
Vào các đối số của riêng chúng.
Phương thức .g()
được định nghĩa bằng @classmethod
.
Điều đó làm cho nó trở thành một class method.
Mỗi phương class method phải có tham số đầu tiên tham chiếu đến class, được gọi là cls
(theo quy ước).
Như trong trường hợp của các instance method, chúng ta không cần cung cấp đối số cho nó khi gọi.
Các instance method có thể truy cập vào class với cls
và các đối số riêng.
Phương thức .h()
được định nghĩa bằng @staticmethod
. Điều này làm cho nó trở thành một static method.
Điều này có nghĩa là các static method có thể truy cập chỉ các đối số của riêng chúng.
Đây là cách các instance method thường được gọi trong Python:
c.f('my-argument')
Kết quả:
Instance method f được gọi
instance: <__main__.MyClass object at 0x039951B0>
instance attributes:
{'x': 2, '_y': 4, '_MyClass__z': 6}
class: <class '__main__.MyClass'>
arg: my-argument
Các class method và static method có thường được gọi thông qua class.
Điều này có nghĩa là bạn không cần phải khởi tạo đối tượng mà vẫn truy cập được.
MyClass.g('my-argument')
Kết quả:
class method g được gọi
cls: <class '__main__.MyClass'>
arg: my-argument
MyClass.h('my-argument')
Kết quả:
static method h được gọi
arg: my-argument
Hãy nhớ là chúng ta không cần truyền đối số tương ứng với tham số đầu tiên của một class method.
Tuy nhiên, các class method và static method cũng có thể được gọi thông qua đối tượng, như thế này:
c.g('my-argument')
Kết quả:
class method g được gọi
cls: <class '__main__.MyClass'>
arg: my-argument
Và,
c.h('my-argument')
Kết quả:
static method h được gọi
arg: my-argument
Khi chúng ta gọi c.g
và c.h
, nếu không có instance member nào có tên như vậy, nó sẽ tìm kiếm đến class member và static member.
Inheritance
Kế thừa (Inheritance) là một tính năng quan trọng khác của lập trình hướng đối tượng.
Nó có một khái niệm trong đó:
-
Một class được gọi là subclass (hoặc class dẫn xuất) sẽ có được (kế thừa) dữ liệu, chức năng từ một số class khác được gọi là super class (hoặc base class).
Trong Python, tất cả các class đều kế thừa từ class được dựng sẵn là object
.
Tuy nhiên, chúng ta có thể định nghĩa hệ thống phân cấp thừa kế của các class riêng của chúng ta cho phù hợp.
Ví dụ: Chúng ta sẽ tạo một class mới gọi là MyOtherClass
kế thừa MyClass
:
class MyOtherClass(MyClass):
def __init__(self, u, v, w, x, y, z):
super().__init__(x, y, z)
self.__u, self.__v, self.__w = u, v, w
def f_(self, arg):
print('instance method f_ được gọi')
print(f'instance: {self}')
print(f'instance attributes:\n{vars(self)}')
print(f'class: {type(self)}')
print(f'arg: {arg}')
d = MyOtherClass(1, 2, 4, 8, 16, 32)
MyOtherClass
có các thành viên của MyClass
: .x
, ._y
, .__z
và .f()
.
Các data member của super class là: .x
, ._y
và .__z
được khởi tạo với câu lệnh super().__init__(x, y, z)
gọi đến phương thức .__ init __()
của super class.
MyOtherClass
cũng có các data member riêng là: .__u
, .__v
, .__w
và .f_()
.
Chúng ta sẽ nhận được các data member với vars()
:
print(vars(d))
Kết quả:
{'x': 8, '_y': 16, '_MyClass__z': 32, '_MyOtherClass__u': 1, '_MyOtherClass__v': 2, '_MyOtherClass__w': 4}
Chúng ta có thể gọi các phương thức từ cả super class và subclass:
-
Gọi phương thức
f
của super class
d.f('some-argument')
Kết quả:
Instance method f được gọi
instance: <__main__.MyOtherClass object at 0x010E5310>
instance attributes:
{'x': 8, '_y': 16, '_MyClass__z': 32, '_MyOtherClass__u': 1, '_MyOtherClass__v': 2, '_MyOtherClass__w': 4}
class: <class '__main__.MyOtherClass'>
arg: some-argument
-
Gọi phương thức
f_
của subclass
d.f_('some-argument')
Kết quả:
instance method f_ được gọi
instance: <__main__.MyOtherClass object at 0x01575310>
instance attributes:
{'x': 8, '_y': 16, '_MyClass__z': 32, '_MyOtherClass__u': 1, '_MyOtherClass__v': 2, '_MyOtherClass__w': 4}
class: <class '__main__.MyOtherClass'>
rg: some-argument
Tuy nhiên, gọi như vậy, nếu subclass và super class có data member nào trùng tên thì subclass sẽ được ưu tiên.
Chúc mừng bạn đã biết cơ bản về Lập trình hướng đối tượng trong Python
Lập trình hướng đối tượng là một trong những mô hình lập trình được Python hỗ trợ. Đây cũng là mô hình lập trình được sử dụng phổ biến nhất.
Ngoài ra, vẫn có những mô hình lập trình khác có ưu điểm trong một số trường hợp khác.
Bài viết này mình đã minh họa cách sử dụng các class Python và tạo các chương trình hướng đối tượng cơ bản.
Có có nhiều thứ về lập trình hướng đối tượng chưa được giải thích rõ như:
-
Methods
.__repr__()
and .__str__()
-
Methods
.__getattribute__()
, .__getattr__()
, .__setattr__()
, và .__delattr__()
-
Abstract classes and members
-
Data classes và nhiều hơn nữa.
Lập trình hướng đối tượng rất quan trọng trong sự nghiệp lập trình viên (Không chỉ lập trình viên Python).
Vì thế, nếu bạn muốn Học Python để tìm kiếm một sự nghiệp với Python thì hãy học và luyện tập sử dụng mô hình này thật kỹ càng.
Chúc bạn thành công!
---
HỌC VIỆN ĐÀO TẠO CNTT NIIT - ICT HÀ NỘI
Dạy học Lập trình chất lượng cao (Since 2002). Học làm Lập trình viên. Hành động ngay!
Đc: Tầng 3, 25T2, N05, Nguyễn Thị Thập, Cầu Giấy, Hà Nội
SĐT: 02435574074 - 0914939543 - 0353655150
Email: hello@niithanoi.edu.vn
Fanpage: https://facebook.com/NIIT.ICT/
#niit #niithanoi #niiticthanoi #hoclaptrinh #khoahoclaptrinh #hoclaptrinhjava #hoclaptrinhphp #python #java #php