Cộng đồng chia sẻ tri thức Lib24.vn

Python - Hướng đối tượng

Gửi bởi: Phạm Thọ Thái Dương 19 tháng 2 2020 lúc 16:43:43


Mục lục
* * * * *

Python đã là một ngôn ngữ hướng đối tượng kể từ khi nó tồn tại. Bởi vì điều này, việc tạo và sử dụng các lớp và các đối tượng hoàn toàn dễ dàng. Chương này giúp bạn trở thành một chuyên gia trong việc sử dụng hỗ trợ lập trình hướng đối tượng của Python.

Nếu bạn không có bất kỳ kinh nghiệm nào trước đây về lập trình hướng đối tượng (OO), bạn có thể muốn tham khảo một khóa học giới thiệu về nó hoặc ít nhất là một hướng dẫn nào đó để bạn nắm được các khái niệm cơ bản.

Tuy nhiên, đây là phần giới thiệu nhỏ về Lập trình hướng đối tượng (OOP) để mang đến cho bạn tốc độ -

Tổng quan về thuật ngữ OOP

  1. Lớp - Một nguyên mẫu do người dùng định nghĩa cho một đối tượng xác định một tập các thuộc tính đặc trưng cho bất kỳ đối tượng nào của lớp. Các thuộc tính là thành viên dữ liệu (biến lớp và biến thể hiện) và phương thức, được truy cập thông qua ký hiệu dấu chấm.
  2. Biến lớp - Một biến được chia sẻ bởi tất cả các phiên bản của một lớp. Các biến lớp được định nghĩa trong một lớp nhưng bên ngoài bất kỳ phương thức nào của lớp. Các biến lớp không được sử dụng thường xuyên như các biến thể hiện.
  3. Thành viên dữ liệu - Một biến lớp hoặc biến thể hiện chứa dữ liệu được liên kết với một lớp và các đối tượng của nó.
  4. Quá tải chức năng - Việc gán nhiều hơn một hành vi cho một chức năng cụ thể. Các hoạt động được thực hiện khác nhau bởi các loại đối tượng hoặc đối số liên quan.
  5. Biến sơ thẩm - Một biến được định nghĩa bên trong một phương thức và chỉ thuộc về thể hiện hiện tại của một lớp.
  6. Kế thừa - Việc chuyển các đặc tính của một lớp sang các lớp khác có nguồn gốc từ nó.
  7. Sơ thẩm - Một đối tượng riêng lẻ của một lớp nhất định. Ví dụ, một đối tượng thuộc về Vòng tròn lớp, là một thể hiện của Vòng tròn lớp.
  8. Khởi tạo - Việc tạo một thể hiện của một lớp.
  9. Phương thức - Một loại hàm đặc biệt được định nghĩa trong định nghĩa lớp.
  10. Đối tượng - Một thể hiện duy nhất của cấu trúc dữ liệu được xác định bởi lớp của nó. Một đối tượng bao gồm cả các thành viên dữ liệu (biến lớp và biến thể hiện) và phương thức.
  11. Quá tải toán tử - Việc gán nhiều hơn một chức năng cho một toán tử cụ thể.

Tạo lớp

Câu lệnh lớp tạo ra một định nghĩa lớp mới. Tên của lớp ngay sau lớp từ khóa được theo sau bởi dấu hai chấm như sau -

class ClassName:
   'Optional class documentation string'
   class_suite
  1. Lớp này có một chuỗi tài liệu, có thể được truy cập thông qua ClassName .__ doc__ .
  2. Các class_suite bao gồm tất cả các báo cáo thành phần xác định các thành viên lớp, thuộc tính dữ liệu và chức năng.

Thí dụ

Sau đây là ví dụ về một lớp Python đơn giản -

class Employee:
   'Common base class for all employees'
   empCount = 0

   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary
  1. Biến empCount là một biến lớp có giá trị được chia sẻ giữa tất cả các phiên bản của lớp này. Điều này có thể được truy cập như Employee.empCount từ bên trong lớp hoặc bên ngoài lớp.
  2. Phương thức đầu tiên __init __ () là một phương thức đặc biệt, được gọi là phương thức khởi tạo lớp hoặc phương thức khởi tạo mà Python gọi khi bạn tạo một thể hiện mới của lớp này.
  3. Bạn khai báo các phương thức lớp khác như các hàm bình thường với ngoại lệ rằng đối số đầu tiên cho mỗi phương thức là tự . Python thêm đối số tự vào danh sách cho bạn; bạn không cần đưa nó vào khi bạn gọi các phương thức.

Tạo đối tượng sơ thẩm

Để tạo các thể hiện của một lớp, bạn gọi lớp bằng tên lớp và truyền vào bất kỳ đối số nào mà phương thức __init__ của nó chấp nhận.

"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)

Truy cập các thuộc tính

Bạn truy cập các thuộc tính của đối tượng bằng cách sử dụng toán tử dấu chấm với đối tượng. Biến lớp sẽ được truy cập bằng tên lớp như sau -

emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

Bây giờ, đặt tất cả các khái niệm lại với nhau -

#!/usr/bin/python

class Employee:
   'Common base class for all employees'
   empCount = 0

   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

Name :  Zara ,Salary:  2000
Name :  Manni ,Salary:  5000
Total Employee 2

Bạn có thể thêm, xóa hoặc sửa đổi các thuộc tính của các lớp và các đối tượng bất cứ lúc nào -

emp1.age = 7  # Add an 'age' attribute.
emp1.age = 8  # Modify 'age' attribute.
del emp1.age  # Delete 'age' attribute.

Thay vì sử dụng các câu lệnh thông thường để truy cập các thuộc tính, bạn có thể sử dụng các hàm sau -

  1. Các getattr (obj, tên [, mặc định]) - để truy cập thuộc tính của đối tượng.
  2. Các hasattr (obj, tên) - để kiểm tra xem một thuộc tính tồn tại hay không.
  3. Các setattr (obj, tên, giá trị) - để thiết lập một thuộc tính. Nếu thuộc tính không tồn tại, thì nó sẽ được tạo.
  4. Các delattr (obj, tên) - để xóa một thuộc tính.
hasattr(emp1, 'age')    # Returns true if 'age' attribute exists
getattr(emp1, 'age')    # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age')    # Delete attribute 'age'

Thuộc tính lớp tích hợp

Mỗi lớp Python tiếp tục theo dõi các thuộc tính tích hợp và chúng có thể được truy cập bằng toán tử dấu chấm như bất kỳ thuộc tính nào khác -

  1. __dict__ - Từ điển chứa không gian tên của lớp.
  2. __doc__ - Chuỗi tài liệu lớp hoặc không có, nếu không xác định.
  3. __name__ - Tên lớp.
  4. __module__ - Tên mô-đun trong đó lớp được xác định. Thuộc tính này là "__main__" trong chế độ tương tác.
  5. __base__ - Một bộ dữ liệu trống có thể chứa các lớp cơ sở, theo thứ tự xuất hiện của chúng trong danh sách lớp cơ sở.

Đối với lớp trên, chúng ta hãy cố gắng truy cập tất cả các thuộc tính này -

#!/usr/bin/python

class Employee:
   'Common base class for all employees'
   empCount = 0

   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount':
<function displayCount at 0xb7c84994>, 'empCount': 2, 
'displayEmployee': <function displayEmployee at 0xb7c8441c>, 
'__doc__': 'Common base class for all employees', 
'__init__': <function __init__ at 0xb7c846bc>}

Tiêu diệt đối tượng (Bộ sưu tập rác)

Python tự động xóa các đối tượng không cần thiết (kiểu tích hợp hoặc thể hiện lớp) để giải phóng không gian bộ nhớ. Quá trình Python định kỳ lấy lại các khối bộ nhớ không còn sử dụng được gọi là Bộ sưu tập Rác.

Trình thu gom rác của Python chạy trong khi thực hiện chương trình và được kích hoạt khi số tham chiếu của đối tượng bằng không. Số tham chiếu của một đối tượng thay đổi khi số lượng bí danh trỏ đến nó thay đổi.

Số tham chiếu của một đối tượng tăng lên khi nó được gán một tên mới hoặc được đặt trong một thùng chứa (danh sách, bộ dữ liệu hoặc từ điển). Số tham chiếu của đối tượng giảm khi bị xóa bằng del , tham chiếu của đối tượng được gán lại hoặc tham chiếu của đối tượng nằm ngoài phạm vi. Khi số tham chiếu của một đối tượng bằng 0, Python sẽ tự động thu thập nó.

a = 40      # Create object <40>
b = a       # Increase ref. count  of <40> 
c = [b]     # Increase ref. count  of <40> 

del a       # Decrease ref. count  of <40>
b = 100     # Decrease ref. count  of <40> 
c[0] = -1   # Decrease ref. count  of <40>

Bạn thường sẽ không nhận thấy khi trình thu gom rác phá hủy một cá thể mồ côi và lấy lại không gian của nó. Nhưng một lớp có thể thực hiện phương thức đặc biệt __del __ () , được gọi là hàm hủy, được gọi khi thể hiện sắp bị hủy. Phương pháp này có thể được sử dụng để dọn sạch mọi tài nguyên không có bộ nhớ được sử dụng bởi một thể hiện.

Thí dụ

Hàm hủy __del __ () này in tên lớp của một cá thể sắp bị hủy -

#!/usr/bin/python

class Point:
   def __init__( self, x=0, y=0):
      self.x = x
      self.y = y
   def __del__(self):
      class_name = self.__class__.__name__
      print class_name, "destroyed"

pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3

Khi đoạn mã trên được thực thi, nó tạo ra kết quả như sau -

3083401324 3083401324 3083401324
Point destroyed

Lưu ý - Tốt nhất, bạn nên xác định các lớp của mình trong tệp riêng biệt, sau đó bạn nên nhập chúng trong tệp chương trình chính của mình bằng cách sử dụng câu lệnh nhập .

Kế thừa giai cấp

Thay vì bắt đầu từ đầu, bạn có thể tạo một lớp bằng cách lấy nó từ một lớp có sẵn bằng cách liệt kê lớp cha trong ngoặc đơn sau tên lớp mới.

Lớp con kế thừa các thuộc tính của lớp cha của nó và bạn có thể sử dụng các thuộc tính đó như thể chúng được định nghĩa trong lớp con. Một lớp con cũng có thể ghi đè các thành viên dữ liệu và phương thức từ cha mẹ.

Cú pháp

Các lớp dẫn xuất được khai báo giống như lớp cha của chúng; tuy nhiên, một danh sách các lớp cơ sở để kế thừa được đưa ra sau tên lớp -

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

Thí dụ

#!/usr/bin/python

class Parent:        # define parent class
   parentAttr = 100
   def __init__(self):
      print "Calling parent constructor"

   def parentMethod(self):
      print 'Calling parent method'

   def setAttr(self, attr):
      Parent.parentAttr = attr

   def getAttr(self):
      print "Parent attribute :", Parent.parentAttr

class Child(Parent): # define child class
   def __init__(self):
      print "Calling child constructor"

   def childMethod(self):
      print 'Calling child method'

c = Child()          # instance of child
c.childMethod()      # child calls its method
c.parentMethod()     # calls parent's method
c.setAttr(200)       # again call parent's method
c.getAttr()          # again call parent's method

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200

Tương tự, bạn có thể lái một lớp từ nhiều lớp cha như sau -

class A:        # define your class A
.....

class B:         # define your class B
.....

class C(A, B):   # subclass of A and B
.....

Bạn có thể sử dụng các hàm hàmububub () hoặc isinstance () để kiểm tra mối quan hệ của hai lớp và thể hiện.

  1. Các issubclass (sub, sup) boolean trở về chức năng true nếu lớp con cho phụ thực sự là một lớp con của lớp cha sup .
  2. Hàm boolean isinstance (obj, Class) trả về true nếu obj là một thể hiện của Class class hoặc là một thể hiện của một lớp con của Class

Phương pháp ghi đè

Bạn luôn có thể ghi đè các phương thức lớp cha của bạn. Một lý do để ghi đè các phương thức của cha mẹ là vì bạn có thể muốn chức năng đặc biệt hoặc khác biệt trong lớp con của mình.

Thí dụ

#!/usr/bin/python

class Parent:        # define parent class
   def myMethod(self):
      print 'Calling parent method'

class Child(Parent): # define child class
   def myMethod(self):
      print 'Calling child method'

c = Child()          # instance of child
c.myMethod()         # child calls overridden method

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

Calling child method

Phương pháp quá tải cơ sở

Bảng sau liệt kê một số chức năng chung mà bạn có thể ghi đè trong các lớp của riêng bạn -

Vận hành quá tải

Giả sử bạn đã tạo một lớp Vector để biểu diễn các vectơ hai chiều, điều gì xảy ra khi bạn sử dụng toán tử cộng để thêm chúng? Nhiều khả năng Python sẽ mắng bạn.

Tuy nhiên, bạn có thể định nghĩa phương thức __add__ trong lớp của mình để thực hiện phép cộng vector và sau đó toán tử cộng sẽ hoạt động theo kỳ vọng -

Thí dụ

#!/usr/bin/python

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b

   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   
   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

Vector(7,8)

Ẩn dữ liệu

Các thuộc tính của một đối tượng có thể hoặc không thể nhìn thấy bên ngoài định nghĩa lớp. Bạn cần đặt tên các thuộc tính với tiền tố gạch dưới kép và các thuộc tính đó không được hiển thị trực tiếp cho người ngoài.

Thí dụ

Bản thử trực tiếp

#!/usr/bin/python

class JustCounter:
   __secretCount = 0
  
   def count(self):
      self.__secretCount += 1
      print self.__secretCount

counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

1
2
Traceback (most recent call last):
   File "test.py", line 12, in <module>
      print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'

Python bảo vệ các thành viên đó bằng cách thay đổi tên bên trong để bao gồm tên lớp. Bạn có thể truy cập các thuộc tính như object._groupName__attrName . Nếu bạn sẽ thay thế dòng cuối cùng của bạn như sau, thì nó hoạt động cho bạn -

.........................
print counter._JustCounter__secretCount

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau -

1
2
2

Được cập nhật: 26 tháng 3 lúc 4:03:43 | Lượt xem: 879