Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Cách sử dụng Protocol trong Python
Trong bài viết này, bạn sẽ tìm hiểu về Python Protocol, một tính năng quan trọng cho phép bạn định nghĩa các giao diện ngầm (implicit interfaces) trong Python. Protocol giúp bạn tạo ra các hàm hoặc lớp linh hoạt hơn, có thể hoạt động với nhiều loại đối tượng khác nhau mà không cần quan tâm đến kiểu dữ liệu cụ thể, chỉ cần các đối tượng đó có các thuộc tính hoặc phương thức cần thiết. Qua việc sử dụng Protocol, bạn có thể xây dựng các chương trình dễ bảo trì và mở rộng hơn.
Giới thiệu về Python Protocol
Giả sử bạn có một hàm tính tổng giá trị của một danh sách sản phẩm, mỗi sản phẩm có các thuộc tính bao gồm tên, số lượng và giá:
from typing import List class Product: def __init__(self, name: str, quantity: float, price: float): self.name = name self.quantity = quantity self.price = price def calculate_total(items: List[Product]) -> float: return sum([item.quantity * item.price for item in items])
Trong ví dụ này, hàm calculate_total()
nhận vào một danh sách các đối tượng Product
và trả về tổng giá trị.
Tuy nhiên, nếu bạn muốn hàm này linh hoạt hơn để có thể tính tổng giá trị cho các danh sách khác ngoài sản phẩm, bạn có thể sử dụng Protocol từ module typing
trong Python.
Bài viết này được đăng tại [free tuts .net]
Sử dụng Python Protocol
Khi nhìn vào hàm calculate_total()
, bạn có thể thấy rằng nó chỉ sử dụng hai thuộc tính là quantity
(số lượng) và price
(giá). Để làm cho hàm này linh hoạt hơn, ta có thể sử dụng Protocol
để định nghĩa một giao diện cho các đối tượng có hai thuộc tính này mà không cần kế thừa từ một lớp cụ thể.
Protocol
đã có sẵn từ Python 3.8, theo mô tả trong PEP 544.
Cách sử dụng Protocol
Đầu tiên, định nghĩa một lớp Item
kế thừa từ Protocol
, với hai thuộc tính là quantity
và price
:
class Item(Protocol): quantity: float price: float
Tiếp theo, sửa đổi hàm calculate_total()
để nhận danh sách các đối tượng Item
thay vì danh sách các đối tượng Product
:
def calculate_total(items: List[Item]) -> float: return sum([item.quantity * item.price for item in items])
Bằng cách này, bạn có thể truyền bất kỳ danh sách đối tượng nào có hai thuộc tính quantity
và price
vào hàm calculate_total()
.
Ví dụ hoàn chỉnh
from typing import List, Protocol class Item(Protocol): quantity: float price: float class Product: def __init__(self, name: str, quantity: float, price: float): self.name = name self.quantity = quantity self.price = price def calculate_total(items: List[Item]) -> float: return sum([item.quantity * item.price for item in items]) # Tính tổng danh sách sản phẩm total = calculate_total([ Product('A', 10, 150), Product('B', 5, 250) ]) print(total) # 2750
Áp dụng Protocol với các lớp khác
Bạn có thể định nghĩa một danh sách hàng tồn kho và truyền nó vào hàm calculate_total()
như sau:
class Stock: def __init__(self, product_name, quantity, price): self.product_name = product_name self.quantity = quantity self.price = price # Tính tổng danh sách hàng tồn kho total = calculate_total([ Stock('Tablet', 5, 950), Stock('Laptop', 10, 850) ]) print(total) # 13500
Trong ví dụ này, lớp Product
và Stock
không cần kế thừa từ Item
, nhưng chúng vẫn có thể được sử dụng trong hàm calculate_total()
.
Duck Typing trong Python
Điều này được gọi là duck typing trong Python. Theo duck typing, hành vi và thuộc tính của đối tượng xác định loại của nó, thay vì kiểu tường minh của đối tượng. Nghĩa là, bất kỳ đối tượng nào có thuộc tính quantity
và price
đều tuân theo giao thức Item
, bất kể kiểu tường minh của nó là gì.
Duck typing xuất phát từ nguyên tắc "kiểm tra con vịt":
Nếu nó đi như một con vịt và kêu như một con vịt, thì nó là một con vịt.
Trong thực tế, khi bạn viết một hàm chấp nhận dữ liệu đầu vào, điều quan trọng hơn là hành vi và thuộc tính của dữ liệu đầu vào chứ không phải kiểu cụ thể của nó.
Kết bài
Sử dụng Python Protocol giúp bạn định nghĩa các giao diện ngầm, tạo ra các hàm và lớp có tính linh hoạt cao mà vẫn đảm bảo đối tượng tuân thủ những yêu cầu về thuộc tính cần thiết. Điều này không chỉ giúp mã nguồn dễ bảo trì, mở rộng mà còn tối ưu hóa việc tái sử dụng, đặc biệt khi làm việc với nhiều loại đối tượng khác nhau mà không cần phụ thuộc vào kiểu dữ liệu cụ thể. Bằng cách tập trung vào các thuộc tính và hành vi của đối tượng, Python Protocol giúp cho việc phát triển phần mềm trở nên rõ ràng và hiệu quả hơn.