images
16/10/2020 12:46 am

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

Đây là phương pháp lập trình nhóm lại các logic của chương trình thành các đối tượng. Mỗi đối tượng là tập hợp các thuộc tính và hành vi tương ứng.

Lập trình hướng đối tượng: Object Oriented Programming


Đây là phương pháp lập trình nhóm lại các logic của chương trình thành các đối tượng. Mỗi đối tượng là tập hợp các thuộc tính và hành vi tương ứng. Đây là cách mô tả lại thế giới thực tế. Ví dụ chúng ta có đối tượng là Dog: Dog sẽ có thuộc tính là tên, tuổi, chiều cao, cân nặng, có hành vi là: sủa, đi, ăn…


Do đó hầu hết các ứng dụng lập trình chúng ta có thể tổ chức thành các thành phần (component) là các đối tượng khác nhau. Mỗi đối tượng có đặc trưng về thuộc tính và hành vi riêng biệt.


Class & Object


Classes – là định nghĩa về các hàm (thủ tục - hành vi) với các thuộc tính với kiểu dữ liệu được xác định


Object – là thể hiện cụ thể của một class


Ví dụ: Chúng ta có class Dog, còn từng đối tượng Dog cụ thể như: Max, Lucy, Charlie là từng object cụ thể của class Dog


Chúng ta tạo ra lớp Dog chỉ có hành vi speak. Hành vi được thể hiện là một hàm trong Python:


class Dog:

     def speak(self):

          print("Bark")


Việc khởi tạo đối tượng ở lớp Dog được làm như sau:


max = Dog()

max.speak()


Ở đây là có class Dog và có object max.


Để thêm thuộc tính cho class, chúng ta cần sử dụng hàm __init__ như sau:

Trường hợp này ta tạo ra 2 thuộc tính là name và age. Để gắn thuộc tính này vào lớp ta cần sử dụng biến self. self là keyword để chỉ đến đối tượng cụ thể hiện tại ở thời điểm chạy.


class Dog:

      def __init__(self, input_name, input_age):

          self.name = input_name, input_age


Khởi tạo đối tượng ở lớp Dog như sau:

max = Dog("Max", 2)

Trường hợp này ta tạo ra đối tượng Dog có tên là Max, tuổi là 2 và gán vào 1 biến 

tên max


Khởi tạo đối tượng ở lớp Dog

max = Dog("Max", 2)


Lúc này method __init__ ở trên sẽ được gọi với 2 tham số. Lúc này “Max" và 2 sẽ được gán vào biến name và age của self. self ở đây chỉ đến đối tượng đang tồn tại, là max.


Vậy làm sao để lấy tên của đối tượng ta mới tạo ra? ở đây ta sẽ thêm phương thức để lấy ra tên:


class Dog:

      def __init__(self, input_name, input_age):

          self.name = input_name, input_age


        def get_name(self):

             return self.name


Trong python, khi khai báo class, mỗi phương thức trong class phải có self là tham số - là tham số đặc biệt. Khi gọi phương thức get_name() ta không cần truyền tham số.


max = Dog("Max", 2)

lucy = Dog("Lucy", 3)


print(max.get_name())

print(lucy.get_name())


Trường hợp này ta tạo ra 2 đối tượng của class Dog và gọi phương thức trả về tên của từng đối tượng


Các thuộc tính built-in của class


class Dog:

   'Common base class for Dog'

   def __init__(self, input_name, input_age):

       self.name = input_name, input_age

   def get_name(self):

       return self.name

print("Dog.__doc__:", Dog.__doc__)

print("Dog.__name__:", Dog.__name__)

print("Dog.__module__:", Dog.__module__)

print("Dog.__bases__:", Dog.__bases__)

print("Dog.__dict__:", Dog.__dict__)


Kết quả chạy:


Dog.__doc__: Common base class for Dog

Dog.__name__: Dog

Dog.__module__: __main__

Dog.__bases__: (<class '__main__.Animal'>,)

Dog.__dict__: {'__module__': '__main__', '__doc__': 'Common base class for Dog', '__init__': <function Dog.__init__ at 0x10820be18>, 'get_name': <function Dog.get_name at 0x10820bd08>}


Ẩn đi thuộc tính


Trong Python, để truy cập vào thuộc tính của một đối tượng, ta có thể truy cập trực tiếp, điều này khác với C++ và Java khi có quy định về khả năng truy cập:


class Dog:

   def __init__(self, input_name):

       self.name = input_name

   def get_name(self):

       return self.name


dog = Dog(‘laika')

print(dog.name)  # Giống dog.get_name()


Để đáp ứng lại được việc này, Python cung cấp thuộc tính dạng “internal" cho phép giấu đi việc truy cập trực tiếp vào thuộc tính. Thuộc tính internal bắt đầu bằng __ như sau


class Dog:

   def __init__(self, input_name):

       self.__name = input_name

   def get_name(self):

       return self.__name


dog = Dog(‘laika')

print(dog.__name) 


Trường hợp này khi chạy, bạn sẽ dính lỗi sau: 


AttributeError: 'Dog' object has no attribute '__name'


Trường hợp này muốn truy cập, bạn cần sử dụng cú pháp: _ClassName__attribute_name


dog = Dog(‘laika')

print(dog._Dog__name) 


Override __str__()


Khi bạn muốn in thông tin một Object ra màn hình, bạn có thể dùng hàm print. Tuy nhiên với đối tượng trong Python, bạn cần phải định nghĩa hàm __str__(). Mỗi class trong Python đều được định nghĩa hàm này, việc của bạn là phải override lại. (Ghi đè). Hãy xem ví dụ sau:


- Sử dụng hàm mặc định: Lúc này object được ghi ra với id, khó biết được thông tin của object là gì:


print(dog) 

Kết quả:

<__main__.Dog object at 0x10cef1828>


- Override lại hàm __str__() trong class Dog: Ta bổ sung thêm thông tin name, hàm này được gọi khi ta gọi hàm str(dog)


class Dog(Animal):

   def __init__(self, name):

       self.__name = name

   def __str__(self):

       return "Dog:" + self.__name

dog = Dog(‘laika')

print(dog) # tương đương print(str(dog))

Kết quả:

Dog:laika


Xóa object


Ta đã quen thuộc với từ khóa del để xóa biến. Muốn xóa đối tượng, ta làm tương tự:


laika = Dog()

del laika


Tuy nhiên, ta cũng có thể biết một object khi nào bị xoá thông qua method __del__()


class Dog:

   'Common base class for Dog'

   ...

   def __del__(self):

      print("This dog is deleted”)

laika = Dog()

del laika


Khi gọi dòng này, method __del__ sẽ được gọi:

This dog is deleted

      

Kế thừa


Trong lập trình hướng đối tượng, kế thừa là tính chất rất quan trọng và giúp cho chương trình có thể thừa hưởng logic đã được định nghĩa trước. Chúng ta cùng xem ví dụ sau. Ta định nghĩa lớp Animal:


class Animal:

   def __init__(self, input_name, input_age):

       self.name = input_name

       self.age = input_age

   def get_name(self):

       return self.name


Dưới đây là định nghĩa lớp Dog là lớp con của lớp Animal. Khi ta định nghĩa lớp con, lớp con sẽ được thừa hưởng toàn bộ thuộc tính của lớp cha:


class Dog(Animal):

   pass


Trường hợp này, do tính kế thừa từ lớp cha Dog sẽ có 2 thuộc tính là name, age. Và có phương thức get_name.


Chúng ta thử chạy lệnh sau:


if __name__ == "__main__":

   animal = Animal("lucy", 1)

   print(animal.get_name())

   max = Dog("max", 1)

   print(max.get_name())



Kết quả:

     Lucy

     max


Giờ chúng ta hãy thử thay đổi phương thức get_name ở class Dog. Trường hợp này ta nói phương thức get_name được override lên phương thức ở trong class Animal:


class Dog(Animal):

   def get_name(self):

       return "I'm a dog with name:" + self.name


Kết quả:

     lucy

     I'm a dog with name: max


Truy cập vào phương thức của lớp cha


Giả sử chúng ta muốn truy cập phương thức lớp cha khi đang ở phương thức của lớp con. Ta sẽ dùng method super() như sau:


class Animal:

       …

   def speak(self):

       return "Hello techzonefun"

class Dog(Animal):

   def get_name(self):

       print(super().speak())

       return "I'm a dog with name:" + self.name


Trường hợp này method speak() của lớp cha Animal được gọi ở lớp con. Ngoài ra do tính kế thừa, nên ta có thể gọi method ở lớp cha nếu lớp con một cách trực tiếp thông qua self nếu lớp con chưa định nghĩa method speak():


class Dog(Animal):

   def get_name(self):

       print(self.speak())       # Gọi đến phương thức lớp cha

       return self.name


Biến static


Biến static hay biến tĩnh trong class là biến thuộc về class mà không thuộc về đối tượng cụ thể. Ta hãy xem ví dụ sau:


class Car:

   number_of_car = 0

   def __init__(self):

       print("new car is created")

       Car.number_of_car = Car.number_of_car + 1


if __name__ == "__main__":

   car1 = Car()

   car2 = Car()

   print(car1.number_of_car)   

   print(car2.number_of_car)

   print(Car.number_of_car)


Ở trên, ta định nghĩa một biến tĩnh (static) number_of_car là biến thuộc về class, do thuộc về class nên nó sẽ được chia sẻ truy cập từ mọi nơi: từ các object khác nhau của class. Để truy cập biến này, ta sử dụng cú pháp <TênClass>.<tên biến>: Car.number_of_car


Do đó kết quả của các dòng lệnh trên sẽ là:

new car is created

new car is created

2

2

2


Phương thức static


Trong class, ta thấy các phương thức phải khai báo tham số self. Đây cũng có thể coi là một cách đánh dấu cho biết phương thức này thuộc về class đang khai báo và truy cập tới các thuộc tính của object hiện tại (self).

Nếu khai báo phương thức không có self, ta sẽ gặp lỗi: 

TypeError: move() takes 0 positional arguments but 1 was given


class Car:

   def move():

       print("moving the car")

if __name__ == "__main__":

   car1 = Car()

   car1.move()


Để định nghĩa một phương thức static trong class, ta có thể sử dụng @staticmethod decorator như sau:


class Car:

   @staticmethod

   def move():

       print("moving the car")

if __name__ == "__main__":

   car1 = Car()

   car1.move()

   Car.move()


Trường hợp này khi chạy ta sẽ có kết quả sau:

moving the car

moving the car


- Trường hợp này, với @staticmethod decorator, ta đã khai báo một phương thức thuộc về class. Giống như biến static, phương thức này gọi là phương thức static, có thể được gọi thẳng từ class hoặc từ các object thể hiện của class.

- Với phương thức này, không thể truy cập được vào các thuộc tính của class (thực tế ta gọi là thuộc tính của các object thể hiện)

- Phương thức này được gọi mà không cần khởi tạo đối tượng của class.


Mời các bạn đọc thêm bài Lập trình đa luồng với Python.


Bạn muốn học Python, cùng tham khảo khóa học này nhé Python cơ bản và ứng dụng thực tế.


- Tech Zone -

Thư giãn chút nào!!!

Bài viết liên quan