تكرار السؤال: …
الإجابة المختصرة
يصف Protocol السلوك المطلوب (دوال و attributes) ويقبل أي class تطابقه هيكليًا دون وراثة صريحة، وهو تمثيل ساكن لمبدأ duck typing.
الشرح
مع ABC يجب أن ترث الـ class صراحةً، أما مع Protocol فيكفي أن تملك الدوال المطلوبة. هذا مفيد لفصل الطبقات: تعرّف الطبقة المستهلكة الواجهة التي تحتاجها، وتطابقها أي implementation دون أن تعرف عنها. حافظ على البروتوكول صغيرًا (دالة أو اثنتان). الفحص ساكن فقط، إلا إذا زيّنته بـ runtime_checkable لاستخدام isinstance، وحتى حينها يتحقق من وجود الأسماء فقط لا من التوقيعات.
مثال بالكود
from typing import Protocol
class Notifier(Protocol):
def send(self, to: str, body: str) -> None: ...
class SmsNotifier: # لا ترث Notifier
def send(self, to: str, body: str) -> None:
...
def notify(n: Notifier, user: str) -> None:
n.send(user, "Welcome")
notify(SmsNotifier(), "+2010") # مقبول هيكليًاأخطاء شائعة
- تعريف Protocol ضخم يربط المستهلك بتفاصيل implementation واحدة.
- الاعتماد على isinstance مع runtime_checkable لاستنتاج تطابق التوقيعات.