Hãy thay đổi ví dụ của chúng ta để cả Hình tròn và Hình chữ nhật đều bắt nguồn từ một lớp tổng quát gọi là Hình học. Lớp này sẽ là một lớp trừu tượng theo nghĩa là nó không được sử dụng để tạo các đối tượng từ. Mục đích của nó là giới thiệu các thuộc tính và khuôn mẫu cho các phương thức mà tất cả các lớp hình học trong dự án của chúng ta đều có điểm chung
class Geometry[]: def __init__[self, x = 0.0, y = 0.0]: self.x = x self.y = y def computeArea[self]: pass def computePerimeter[self]: pass def move[self, deltaX, deltaY]: self.x += deltaX self.y += deltaY def __str__[self]: return 'Abstract class Geometry should not be instantiated and derived classes should override this method!'
Hàm tạo của lớp Hình học trông khá bình thường, nó chỉ khởi tạo các biến thể hiện mà tất cả các đối tượng hình học của chúng ta có điểm chung, cụ thể là tọa độ x và y để mô tả vị trí của chúng trong hệ tọa độ 2D của chúng ta. Tiếp theo là định nghĩa của các phương thức computeArea[], computePerimeter[], move[…] và __str__[] mà tất cả các đối tượng hình học nên hỗ trợ. Đối với move[…], chúng tôi đã có thể cung cấp một triển khai vì nó hoàn toàn dựa trên các biến thể hiện x và y và hoạt động theo cùng một cách cho tất cả các đối tượng hình học. Điều đó có nghĩa là các lớp dẫn xuất cho Hình tròn và Hình chữ nhật sẽ không cần cung cấp triển khai của riêng chúng. Ngược lại, bạn không thể tính diện tích hoặc chu vi một cách có ý nghĩa chỉ từ vị trí của đối tượng. Do đó, chúng tôi đã sử dụng từ khóa pass để cho biết rằng chúng tôi đang cố ý để trống phần thân của các phương thức computeArea[] và computePerimeter[]. Các phương thức này sẽ phải được ghi đè trong định nghĩa của lớp dẫn xuất bằng cách triển khai hành vi chuyên biệt của chúng. Chúng tôi có thể đã làm điều tương tự cho __str__[] nhưng thay vào đó chúng tôi trả về một thông báo cảnh báo rằng lớp này không nên được khởi tạo.
Điều đáng nói là, trong nhiều ngôn ngữ lập trình hướng đối tượng, các khái niệm về lớp trừu tượng [= lớp không thể khởi tạo] và phương thức trừu tượng [= phương thức phải được ghi đè trong mọi lớp con có thể khởi tạo] là . Điều đó có nghĩa là tồn tại các từ khóa đặc biệt để khai báo một lớp hoặc phương thức là trừu tượng và sau đó không thể tạo một đối tượng của lớp đó hoặc một lớp con của lớp đó mà không cung cấp triển khai cho các phương thức trừu tượng. Trong Python, điều này đã được thêm vào đầu ngôn ngữ thông qua một mô-đun trong thư viện chuẩn có tên là abc [dành cho các lớp cơ sở trừu tượng]. Mặc dù chúng tôi sẽ không sử dụng nó trong khóa học này, nhưng bạn nên kiểm tra và sử dụng nó nếu bạn tham gia vào các dự án Python lớn hơn. Trang Lớp học trừu tượng này là một nguồn tốt để tìm hiểu thêm
Đây là định nghĩa mới của chúng tôi cho lớp Circle hiện được lấy từ lớp Geometry. Chúng tôi cũng sử dụng một vài lệnh ở cuối để tạo và sử dụng một đối tượng Circle mới của lớp này để đảm bảo mọi thứ thực sự hoạt động như trước
import math class Circle[Geometry]: def __init__[self, x = 0.0, y = 0.0, radius = 1.0]: super[Circle,self].__init__[x,y] self.radius = radius def computeArea[self]: return math.pi * self.radius ** 2 def computePerimeter [self]: return 2 * math.pi * self.radius def __str__[self]: return 'Circle with coordinates {0}, {1} and radius {2}'.format[self.x, self.y, self.radius] circle1 = Circle[10, 10, 10] print[circle1.computeArea[]] print[circle1.computePerimeter[]] circle1.move[2,2] print[circle1]
Dưới đây là những điều chúng tôi cần làm trong mã
- Trong dòng 3, chúng tôi đã phải thay đổi tiêu đề của định nghĩa lớp để bao gồm tên của lớp cơ sở mà chúng tôi đang tạo ra Vòng tròn từ ['Hình học'] trong dấu ngoặc đơn
- Hàm tạo của Circle có cùng ba tham số như trước. Tuy nhiên, nó chỉ khởi tạo biến thể hiện mới radius ở dòng 7. Để khởi tạo hai biến còn lại, nó gọi hàm tạo của lớp cơ sở của nó, do đó, lớp Hình học, ở dòng 6 với lệnh “super[Circle,self]. __init__[x,y]”. Điều này có nghĩa là “gọi hàm tạo của lớp cơ sở của lớp Circle và truyền các giá trị của x và y làm tham số cho nó”. Thông thường, nên gọi hàm tạo của lớp cơ sở là lệnh đầu tiên trong hàm tạo của lớp dẫn xuất để tất cả các khởi tạo chung được thực hiện
- Sau đó, chúng tôi cung cấp các định nghĩa về computeArea[] và computePerimeter[] dành riêng cho các vòng kết nối. Các định nghĩa này ghi đè các định nghĩa "trống" của lớp cơ sở Hình học. Điều này có nghĩa là bất cứ khi nào chúng ta gọi computeArea[] hoặc computePerimeter[] cho một đối tượng của lớp Circle, mã từ các định nghĩa chuyên biệt này sẽ được thực thi
- Lưu ý rằng chúng tôi không cung cấp bất kỳ định nghĩa nào cho phương thức move[…] trong định nghĩa lớp này. Điều đó có nghĩa là khi move[…] được gọi cho một đối tượng Circle, mã từ định nghĩa tương ứng trong Geometry lớp cơ sở của nó sẽ được thực thi
- Chúng tôi ghi đè phương thức __str__[] để tạo ra cùng một loại chuỗi có thông tin về tất cả các biến đối tượng mà chúng tôi có trong định nghĩa trước đó. Lưu ý rằng hàm này truy cập cả hai biến thể hiện được định nghĩa trong lớp cha Hình học cũng như biến bổ sung được thêm vào trong định nghĩa của Circle
Định nghĩa mới của lớp Hình chữ nhật, hiện bắt nguồn từ Hình học, trông rất giống với định nghĩa của Hình tròn nếu bạn thay thế “Hình tròn” bằng “Hình chữ nhật”. Chỉ có cách triển khai của các phương thức bị ghi đè trông khác, sử dụng các phiên bản dành riêng cho hình chữ nhật
class Rectangle[Geometry]: def __init__[self, x = 0.0, y = 0.0, width = 1.0, height = 1.0]: super[Rectangle, self].__init__[x,y] self.width = width self.height = height def computeArea[self]: return self.width * self.height def computePerimeter [self]: return 2 * [self.width + self.height] def __str__[self]: return 'Rectangle with coordinates {0}, {1}, width {2} and height {3}'.format[self.x, self.y, self.width, self.height ] rectangle1 = Rectangle[15,20,4,5] print[rectangle1.computeArea[]] print[rectangle1.computePerimeter[]] rectangle1.move[2,2] print[rectangle1]
< 4. 7 Kế thừa, Phân cấp lớp và Đa hình lên 4. 7. 2 Thêm một lớp khác vào hệ thống phân cấp ›