Lập trình hướng đối tượng trong Python

Đồ thị tư duy lập trình hướng đối tượng

Hướng đến: quy trình, hàm, đối tượng

Lập trình hướng quy trình: Viết mã theo logic nghiệp vụ từ trên xuống!

Nhược điểm của lập trình hướng quy trình: Mỗi lần gọi đều phải viết lại, mã rất dài, không có khả năng tái sử dụng, mỗi khi thêm chức năng mới phải sửa tất cả mã!

Vậy làm thế nào để giải quyết những nhược điểm trên? Hàm đã ra đời.

Lập trình hướng hàm: Đóng gói mã chức năng vào hàm, sau này không cần viết lại, chỉ cần gọi hàm!

Giải thích hàm: Hàm có thể hiểu là một khối chức năng, bạn chia một chức năng lớn thành các khối nhỏ, khi cần sử dụng chức năng nào thì gọi khối chức năng đó!

Hàm có thể hiểu là: khối Lego, cho bạn từng khối, bạn có thể sử dụng các khối này để tạo nên bất cứ thứ gì bạn muốn!

Hàm có thể gọi hàm! Chức năng chính là nối các hàm lại với nhau, gọi chúng! Hàm không thể tự thực thi nếu bạn không gọi nó!

Lập trình hướng đối tượng: Tái sử dụng mã đơn giản hơn!

Lập trình hướng đối tượng là gì? Đó là tất cả mã được thực hiện thông qua lớp và đối tượng!

Tạo lớp và đối tượng

Lập trình hướng đối tượng (Object Oriented Programming, OOP)

Java và C# chỉ hỗ trợ lập trình hướng đối tượng, trong khi Python linh hoạt hơn, hỗ trợ cả lập trình hướng đối tượng và lập trình hàm

Tất cả các ngôn ngữ đều có đặc tính: Đóng gói, Kế thừa, Đa hình (đa hình trong Python không thể hiện tốt)

Lập trình hướng đối tượng là một phương thức lập trình: Thực hiện thông qua "lớp" và "đối tượng", do đó, lập trình hướng đối tượng thực chất là sử dụng "lớp" và "đối tượng".

Xem hai ví dụ:

Ví dụ 1:

## Cách 1
# Hàm
def Chao():
    print 'Chào'
def XinChao(ten):
        print 'Tôi là %s' %ten

Chao()
XinChao('Tuan')


# Cách 2
# Tạo lớp
class Nguoi:
     
    def Chao(self):
        print 'Chào'
 
    def XinChao(self, ten):
        print 'Tôi là %s' %ten
 
# Tạo đối tượng obj từ lớp Nguoi
obj = Nguoi()
obj.Chao()            # Thực thi phương thức Chao
obj.XinChao('Tuan') # Thực thi phương thức XinChao 

Cách nào đơn giản hơn? Chắc chắn là cách đầu tiên, tại sao? Không phải lớp sẽ đơn giản hơn sao? Vì chúng ta chưa sử dụng được các tính năng của lớp!

Trước khi học được đóng gói và kế thừa, cách 1 đơn giản hơn!**Ví dụ 2:**Dựa trên cơ sở dữ liệu:Các thao tác phổ biến với cơ sở dữ liệu: Thêm, Xóa, Sửa, TìmThêm, Xóa, Sửa, Tìm có thể viết thành bốn phương thức không?

def Tim():

def Sua():

def Xoa():

def Them()


# Giả sử tôi muốn thêm một giá trị, trong hàm Them có cần:
# Kết nối cơ sở dữ liệu hostname, port, username, password, dbname
# Tôi có cần lưu nhiều thứ này để kết nối không? Sau khi kết nối có cần mở cơ sở dữ liệu không?
# Kết nối cơ sở dữ liệu hostname, port, username, password, dbname
# Mở
# Thao tác
# Đóng

Vậy thêm có cần không? Sửa có cần không? Tìm có cần không? Cả hai đều cần, vậy xem cách lập trình hàm thực tế:

def Tim(hostname, port, username, password, dbname, dieu_kien):
    # Kết nối cơ sở dữ liệu hostname, port, username, password, dbname
    # Mở
    # Thao tác
    # Đóng
def Sua(hostname, port, username, password, dbname, dieu_kien):
    # Kết nối cơ sở dữ liệu hostname, port, username, password, dbname
    # Mở
    # Thao tác
    # Đóng
def Xoa(hostname, port, username, password, dbname, dieu_kien):
    # Kết nối cơ sở dữ liệu hostname, port, username, password, dbname
    # Mở
    # Thao tác
    # Đóng
def Them(hostname, port, username, password, dbname, dieu_kien)
    # Kết nối cơ sở dữ liệu hostname, port, username, password, dbname
    # Mở
    # Thao tác
    # Đóng

Mỗi hàm đều chứa một số trường! Bạn tham số hóa chúng, có cần thêm vào tham số không? Khi thực thi, bạn có cần truyền tham số vào không?
Tim (hostname, port, username, password, dbname, [11,22,33,44])
Sua (hostname, port, username, password, dbname, [11,22,33,44])
Xoa(hostname, port, username, password, dbname, [11,22,33,44])
Them(hostname, port, username, password, dbname, [11,22,33,44])

Mỗi lần thao tác có cần viết lại không?

Cách trên là sử dụng hàm, vậy xem ví dụ dưới đây sử dụng lập trình hướng đối tượng:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

class QuanLyCSDL(object):
    def __init__(self, hostname, port, username, password, dbname): #__init__ là phương thức khởi tạo, thực thi khi khởi tạo
        self.hostname = hostname
        self.port = port
        self.username = username
        self.password = password
        self.dbname = dbname

    def Tim(self):
        # Kết nối cơ sở dữ liệu self.hostname, self.port, self.username, self.password, self.dbname
        # Mở
        # Thao tác
        # Đóng
    def Sua(self):
        # Kết nối cơ sở dữ liệu self.hostname, self.port, self.username, self.password, self.dbname
        # Mở
        # Thao tác
        # Đóng
    def Them(self):
        # Kết nối cơ sở dữ liệu self.hostname, self.port, self.username, self.password, self.dbname
        # Mở
        # Thao tác
        # Đóng
    def Xoa(self):
        # Kết nối cơ sở dữ liệu self.hostname, self.port, self.username, self.password, self.dbname
        # Mở
        # Thao tác
        # Đóng
obj1 = QuanLyCSDL('db.server.com', 3306, 'admin', 'password123', 'mydatabase')

obj1.Tim()
obj1.Sua()
obj1.Them()
obj1.Xoa()

Bây giờ khi gọi phương thức trong lớp, tôi có cần truyền tham số không? Không cần nữa, đặc điểm chính của lập trình hướng đối tượng trong Python là đóng gói!

Vậy các tham số này ở đâu?

obj1 = QuanLyCSDL('db.server.com', 3306, 'admin', 'password123', 'mydatabase') Khi khởi tạo, tôi đã truyền các tham số này cho lớp, chúng được đóng gói trong self

Như hình dưới đây:

Ví dụ: Khi obj1 gọi phương thức Them, trong đối tượng obj1 không chỉ lưu các tham số của đối tượng, mà còn có một tham số ẩn gọi là "con trỏ đối tượng lớp",

Chức năng của nó là chỉ cho đối tượng biết nơi tìm phương thức! Hãy tưởng tượng nếu không có con trỏ đối tượng lớp này, đối tượng có biết nó được tạo từ mẫu nào không? Không biết!

Và đối tượng không có phương thức, phương thức được lưu trong lớp! Như hình trên, mỗi phương thức đều có: self, nghĩa là: obj1 gọi tức là def Them(obj1) !!!

So với lập trình hàm, tôi có cần truyền một loạt tham số mỗi lần gọi không?

Sau khi xem xét các ví dụ trên, bạn sẽ nghĩ: Nếu một số phương thức sử dụng chung một nhóm biến, nếu sử dụng lập trình hàm, mỗi lần gọi phương thức đều phải truyền các tham số này!

Lúc này hãy nghĩ đến sử dụng lập trình hướng đối tượng: Đóng gói các biến này vào đối tượng, mỗi lần gọi chỉ cần truyền đối tượng này!

Ví dụ 3:

Nếu có một trò chơi, tôi muốn tạo nhân vật bằng hàm thì sao? Có cần viết nhiều hàm không? Rất phức tạp, xem mã của tôi!

#!/usr/bin/env python
#-*- coding:utf-8 -*-

'''
Định nghĩa: Mẫu và chức năng cơ bản của nhân vật trong game
'''
import random


class NhanVatGame(object):
    def __init__(self, ten, nghe_nghiep, tan_cong=0, mau=0, toc_do=0): #Hàm khởi tạo, tên và nghề nghiệp
        self.ten = ten   # Định nghĩa trường thông thường
        self.nghe_nghiep = nghe_nghiep # Định nghĩa trường thông thường
        self.tan_cong = tan_cong
        self.mau = mau
        self.toc_do = toc_do

    def nang_cap(self):
        self.mau = self.mau + 1000
        self.tan_cong = self.tan_cong + 1000
        self.toc_do = self.toc_do + 1000
        print "\033[32;1mGọi hỗ trợ, máu hiện tại: %s, tấn công hiện tại: %s, tốc độ hiện tại: %s" % (self.mau, self.tan_cong, self.toc_do)
    def tang_tan_cong(self):
        self.tan_cong = self.tan_cong +300
        print "\033[32;1mSức tấn công hiện tại của bạn là %s\033[0m" % self.tan_cong
    def hoi_phuc(self):
        self.mau = self.mau + 300
        print "\033[32;1mMáu hiện tại của bạn là %s\033[0m" % self.mau
    def chi_tiet(self):
        """Ghi chú: Thông tin chi tiết của đối tượng hiện tại"""
        temp = "Nhân vật: %s ; Nghề nghiệp: %s ; Sức tấn công: %s ; Máu: %s ; Tốc độ: %s"  % (self.ten, self.nghe_nghiep, self.tan_cong, self.mau, self.toc_do)
        print temp

Khi tạo một nhân vật:

obj1 = NhanVatGame('Tên nhân vật','Nghề nghiệp',Sức tấn công,'Máu','Tốc độ')

Bây giờ xem! Bây giờ tôi đã tạo một nhân vật thực sự, nó có tên, nghề nghiệp, tấn công, máu, tốc độ của riêng nó. Nếu viết bằng hàm, có chết không!

Tóm tắt: Tạo động một thứ gì đó, khi tạo phải dựa trên một mẫu, khi bạn tạo động một thứ thuộc về một loại nào đó, hãy sử dụng lập trình hướng đối tượng!

Kế thừa

Kế thừa rất đơn giản, ví dụ, bây giờ tôi có một mẫu nhân vật, tôi có thể tạo các nhân vật khác nhau dựa trên mẫu này, bây giờ tôi có một yêu cầu, tôi muốn viết riêng một mẫu chiến binh

Những thứ trong lớp tôi viết ở trên, nhiều thứ trùng lặp có thể sử dụng, tôi có cần viết lại không? Python cung cấp một phương pháp đơn giản "kế thừa"!

Trước đây đã nói Python hướng đối tượng giúp bạn tái sử dụng mã tốt hơn!

#!/usr/bin/env python
#-*- coding:utf-8 -*-

class NhanVatGame(object):
    def __init__(self, ten, nghe_nghiep, tan_cong=0, mau=0, toc_do=0): #Hàm khởi tạo, tên và nghề nghiệp
        self.ten = ten   # Định nghĩa trường thông thường
        self.nghe_nghiep = nghe_nghiep # Định nghĩa trường thông thường
        self.tan_cong = tan_cong
        self.mau = mau
        self.toc_do = toc_do

    def nang_cap(self):
        self.mau = self.mau + 1000
        self.tan_cong = self.tan_cong + 1000
        self.toc_do = self.toc_do + 1000
        print "\033[32;1mGọi hỗ trợ, máu hiện tại: %s, tấn công hiện tại: %s, tốc độ hiện tại: %s" % (self.mau, self.tan_cong, self.toc_do)
    def tang_tan_cong(self):
        self.tan_cong = self.tan_cong +300
        print "\033[32;1mSức tấn công hiện tại của bạn là %s\033[0m" % self.tan_cong
    def hoi_phuc(self):
        self.mau = self.mau + 300
        print "\033[32;1mMáu hiện tại của bạn là %s\033[0m" % self.mau
    def chi_tiet(self):
        """Ghi chú: Thông tin chi tiết của đối tượng hiện tại"""
        temp = "Nhân vật: %s ; Nghề nghiệp: %s ; Sức tấn công: %s ; Máu: %s ; Tốc độ: %s"  % (self.ten, self.nghe_nghiep, self.tan_cong, self.mau, self.toc_do)
        print temp


class ChienBinh(NhanVatGame):
    def su_dung_vu_khi(self):
        self.tan_cong = self.tan_cong + 1000
        print "\033[32;1mCầm vũ khí, tấn công +1000, sức tấn công hiện tại của bạn là: %s \033[0m"  % self.tan_cong

Trong lớp thứ hai: ChienBinh, tôi không đặt trường tấn_công, nó đến từ đâu! class ChienBinh(NhanVatGame): Ở đây kế thừa các trường và phương thức của lớp cơ sở! Như sau:

Tôi đã tạo một nhân vật từ lớp chiến binh, nhưng tôi không có add_attack() ở đây, nó đến từ đâu? Đó là kế thừa, vậy đó là kế thừa!

Ở đây cần đề cập một điểm, "lớp cổ điển", "lớp mới", và đa kế thừa của nó! (Lớp Python có thể kế thừa nhiều lớp, Java và C# chỉ có thể kế thừa một lớp)

Lớp mới có (object) ở trên, có sự khác biệt gì! Lớp mới, có một số sửa đổi và thêm chức năng mới, sau này nên sử dụng "lớp mới"

Cần đề cập một điểm: Trong trường hợp đa kế thừa:

Dù là kế thừa trực tiếp hay gián tiếp lớp mới, lớp của bạn cũng là lớp mới!

Khi lớp là lớp cổ điển, trong trường hợp đa kế thừa, sẽ tìm theo phương thức ưu tiên độ sâu Khi lớp là lớp mới, trong trường hợp đa kế thừa, sẽ tìm theo phương thức ưu tiên độ rộng

Lớp cổ điển: Đầu tiên tìm trong lớp A, nếu lớp A không có, tiếp tục tìm trong lớp B, nếu lớp B không có, tiếp tục tìm trong lớp D, nếu lớp D không có, tiếp tục tìm trong lớp C, nếu vẫn không tìm thấy, báo lỗi

Lớp mới: Đầu tiên tìm trong lớp A, nếu lớp A không có, tiếp tục tìm trong lớp B, nếu lớp B không có, tiếp tục tìm trong lớp C, nếu lớp C không có, tiếp tục tìm trong lớp D, nếu vẫn không tìm thấy, báo lỗi

Lưu ý: Trong quá trình tìm kiếm trên, một khi tìm thấy, quá trình tìm kiếm sẽ ngay lập tức dừng lại, không tiếp tục tìm nữa

Vậy vấn đề là, nếu trong kế thừa có "lớp cổ điển" và "lớp mới", cái nào ưu tiên? Xem mã và hình dưới đây:

class D(object):
    def bar(self):
        print 'D.bar'

class C(D):
    def bar(self):
        print 'C.bar'

class B(D):
    pass

class A(B,C):
    pass

a = A()
a.bar()

Chỉ trong trường hợp trên là ưu tiên độ rộng!

Tóm tắt

Lập trình hướng đối tượng là một phương thức lập trình, phương thức lập trình này được thực hiện dựa trên việc sử dụng "lớp" và "đối tượng" Lớp là một mẫu, mẫu đóng gói nhiều "hàm" để sử dụng Đối tượng, thực thể được tạo từ mẫu (tức: đối tượng), thực thể được sử dụng để gọi các hàm được đóng gói trong lớp Ba đặc tính chính của lập trình hướng đối tượng: Đóng gói, Kế thừa và Đa hình (đa hình trong Python không thể hiện tốt)

Lập trình hàm và Lập trình hướng đối tượng nên chọn loại nào? Sử dụng trong trường hợp nào?

Đối với lập trình viên C# và Java, không có vấn đề này, vì hai ngôn ngữ này chỉ hỗ trợ lập trình hướng đối tượng (không hỗ trợ lập trình hàm). Đối với Python và PHP và các ngôn ngữ khác, hỗ trợ cả hai phương thức lập trình, và lập trình hàm có thể thực hiện được, lập trình hướng đối tượng cũng có thể thực hiện; và lập trình hướng đối tượng có thể thực hiện được, lập trình hàm không thể (lập trình hàm không thể thực hiện được tính năng đóng gói của lập trình hướng đối tượng).

Vì vậy, trong phát triển Python, thường sử dụng hoàn toàn lập trình hướng đối tượng hoặc kết hợp lập trình hướng đối tượng và lập trình hàm

Thẻ: python oop Lập trình hướng đối tượng class object

Đăng vào ngày 20 tháng 6 lúc 23:54