Người ta cũng có thể thực hiện raise NotImplementedError()
bên trong phương thức con của một @abstractmethod
phương thức lớp cơ sở -decorated.
Hãy tưởng tượng viết một kịch bản điều khiển cho một họ mô-đun đo lường (thiết bị vật lý). Chức năng của mỗi mô-đun được định nghĩa hẹp, chỉ thực hiện một chức năng chuyên dụng: một chức năng có thể là một dãy rơ le, một bộ DAC hoặc ADC đa kênh, một ampe kế khác, v.v.
Phần lớn các lệnh cấp thấp được sử dụng sẽ được chia sẻ giữa các mô-đun, ví dụ như để đọc số ID của chúng hoặc để gửi lệnh cho chúng. Hãy xem những gì chúng ta có vào thời điểm này:
Lớp cơ sở
from abc import ABC, abstractmethod
class Generic(ABC):
''' Base class for all measurement modules. '''
def __init__(self):
def _read_ID(self):
def _send_command(self, value):
Động từ chia sẻ
Sau đó, chúng tôi nhận ra rằng phần lớn các động từ lệnh dành riêng cho mô-đun và do đó, logic của các giao diện của chúng cũng được chia sẻ. Dưới đây là 3 động từ khác nhau mà nghĩa của chúng sẽ tự giải thích được nếu xét theo một số mô-đun đích.
get(channel)
relay: nhận trạng thái bật / tắt của relaychannel
DAC: bật điện áp đầu rachannel
ADC: bật điện áp đầu vàochannel
enable(channel)
relay: cho phép sử dụng relay bậtchannel
DAC: cho phép sử dụng kênh đầu ra trênchannel
ADC: bật sử dụng kênh đầu vàochannel
set(channel)
relay:channel
bật / tắt rơ le
DAC: bật điện áp đầu rachannel
ADC: hmm ... không có gì logic trong đầu.
Động từ chia sẻ trở thành động từ cưỡng bức
Tôi lập luận rằng có một trường hợp mạnh mẽ để các động từ trên được chia sẻ trong các mô-đun vì chúng ta đã thấy rằng ý nghĩa của chúng là hiển nhiên cho mỗi một trong số chúng. Tôi sẽ tiếp tục viết lớp cơ sở của mình Generic
như vậy:
class Generic(ABC):
@abstractmethod
def get(self, channel):
pass
@abstractmethod
def enable(self, channel):
pass
@abstractmethod
def set(self, channel):
pass
Các lớp con
Bây giờ chúng ta biết rằng tất cả các lớp con của chúng ta sẽ phải xác định các phương thức này. Hãy xem nó có thể trông như thế nào đối với mô-đun ADC:
class ADC(Generic):
def __init__(self):
super().__init__()
def get(self, channel):
def enable(self, channel):
Bây giờ bạn có thể tự hỏi:
Nhưng điều này sẽ không hoạt động đối với mô-đun ADC vì set
không có ý nghĩa gì ở đó như chúng ta vừa thấy ở trên!
Bạn nói đúng: không triển khai set
không phải là một tùy chọn vì Python sau đó sẽ gây ra lỗi bên dưới khi bạn cố gắng khởi tạo đối tượng ADC của mình.
TypeError: Can't instantiate abstract class 'ADC' with abstract methods 'set'
Vì vậy, bạn phải triển khai một cái gì đó, bởi vì chúng tôi đã tạo set
một động từ thực thi (hay còn gọi là '@abstractmethod'), được chia sẻ bởi hai mô-đun khác nhưng đồng thời, bạn cũng không được triển khai bất kỳ thứ gì
set
không có ý nghĩa đối với mô-đun cụ thể này.
NotImplementedError to the Rescue
Bằng cách hoàn thành lớp ADC như sau:
class ADC(Generic):
def set(self, channel):
raise NotImplementedError("Can't use 'set' on an ADC!")
Bạn đang làm ba điều rất tốt cùng một lúc:
- Bạn đang bảo vệ người dùng khỏi phát hành sai một lệnh ('set') không (và không nên!) Được triển khai cho mô-đun này.
- Bạn đang nói cho họ biết rõ ràng vấn đề là gì (xem liên kết của TemporalWolf về 'Không có ngoại lệ' để biết lý do tại sao điều này lại quan trọng)
- Bạn đang bảo vệ việc triển khai tất cả các mô-đun khác mà các động từ được thực thi
có ý nghĩa. Tức là bạn đảm bảo rằng những mô-đun mà các động từ này có ý nghĩa sẽ triển khai các phương thức này và chúng sẽ làm như vậy bằng cách sử dụng chính xác các động từ này chứ không phải một số tên đặc biệt khác.