پیاده سازی یک رابط در پایتون
رابط ها نقش مهمی در مهندسی نرم افزار دارند. با رشد یک برنامه، مدیریت به روزرسانی ها و تغییرات در پایگاه کد دشوارتر می شود. بیشتر اوقات، کلاس هایی دارید که بسیار شبیه به هم هستند اما نامرتبط هستند، که می تواند منجر به سردرگمی شود. در این آموزش، خواهید دید که چگونه می توانید از یک رابط پایتون برای کمک به تعیین کلاس برای مقابله با مشکل فعلی استفاده کنید.
در این آموزش می توانید:
- آشنایی با نحوه عملکرد رابط ها و هشدارهای ایجاد رابط پایتون
- درک کنید که رابط ها در یک زبان پویا مانند پایتون چقدر مفید هستند.
- پیاده سازی یک رابط غیررسمی پایتون
- از
abc استفاده کنید. ABCMeta
و@abc.abstractبرای
پیاده سازی یک رابط رسمی پایتون
رابط ها در پایتون متفاوت از بسیاری از زبان های دیگر مدیریت می شوند و می توانند از نظر پیچیدگی طراحی متفاوت باشند. در پایان این آموزش، درک بهتری از برخی جنبه های مدل داده پایتون و همچنین نحوه مقایسه رابط های پایتون با زبان هایی مانند جاوا، C++ و Go خواهید داشت.
بررسی رابط پایتون
در سطح بالا، یک رابط به عنوان یک طرح اولیه برای طراحی کلاس ها عمل می کند. مانند کلاس ها، رابط ها روش ها را تعریف می کنند. برخلاف کلاس ها، این روش ها انتزاعی هستند. روش انتزاعی روشی است که رابط به سادگی تعریف می کند. روش ها را پیاده سازی نمی کند. این کار توسط کلاس هایی انجام می شود که سپس رابط را پیاده سازی می کنند و معنای مشخصی به روش های انتزاعی رابط می دهند.
رویکرد پایتون به طراحی رابط در مقایسه با زبان هایی مانند جاوا، Go و C++ تا حدودی متفاوت است. این زبان ها همگی دارای یک کلمه کلیدی رابط
هستند، در حالی که پایتون ندارد. پایتون بیشتر از یک جنبه دیگر از زبان های دیگر منحرف می شود. نیازی به کلاسی که رابط را پیاده سازی می کند برای تعریف همه متدهای انتزاعی رابط ندارد.
رابط های غیررسمی
در شرایط خاص، ممکن است به قوانین سختگیرانه یک رابط رسمی پایتون نیاز نداشته باشید. ماهیت پویای پایتون به شما امکان می دهد یک رابط غیررسمی را پیاده سازی کنید. یک رابط غیررسمی پایتون کلاسی است که متدهایی را تعریف میکند که میتوان آنها را لغو کرد، اما هیچ اجرای سختگیرانه ای وجود ندارد.
در مثال زیر، دیدگاه یک مهندس داده را در نظر خواهید گرفت که باید متن را از انواع مختلف فایل های بدون ساختار مانند PDF و ایمیل استخراج کند. شما یک رابط غیررسمی ایجاد خواهید کرد که متدهایی را که در هر دو کلاس بتن PdfParser
و EmlParser
وجود دارند تعریف می کند:
class InformalParserInterface:
def load_data_source(self, path: str, file_name: str) -> str:
"""Load in the file for extracting text."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""Extract text from the currently loaded file."""
pass
InformalParserInterface
دو روش .load_data_source()
و .extract_text()
را تعریف می کند. این روش ها تعریف شده اند اما اجرا نمی شوند. پیاده سازی زمانی اتفاق می افتد که کلاس های مشخصی را ایجاد کنید که از InformalParserInterface
به ارث می برند.
همانطور که می بینید، InformalParserInterface
با یک کلاس استاندارد پایتون یکسان به نظر می رسد. شما به تایپ اردک تکیه می کنید تا به کاربران اطلاع دهید که این یک رابط کاربری است و باید بر اساس آن استفاده شود.
توجه: در مورد تایپ اردک چیزی نشنیده اید؟ این اصطلاح می گوید که اگر شما جسمی دارید که شبیه اردک است، مانند اردک راه می رود و مانند اردک درز می کند، پس باید اردک باشد! برای کسب اطلاعات بیشتر، Duck Typing را بررسی کنید.
با در نظر گرفتن تایپ اردک، شما دو کلاس را تعریف می کنید که InformalParserInterface
را پیاده سازی می کنند. برای استفاده از رابط کاربری خود، باید یک کلاس بتن ایجاد کنید. کلاس بتن زیرمجموعه ای از رابط است که پیاده سازی متدهای رابط را فراهم می کند. شما دو کلاس بتن برای پیاده سازی رابط کاربری خود ایجاد خواهید کرد. اولین مورد PdfParser
است که از آن برای تجزیه متن از فایل های PDF استفاده خواهید کرد:
class PdfParser(InformalParserInterface):
"""Extract text from a PDF"""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides InformalParserInterface.load_data_source()"""
pass
def extract_text(self, full_file_path: str) -> dict:
"""Overrides InformalParserInterface.extract_text()"""
pass
پیاده سازی مشخص InformalParserInterface
اکنون به شما امکان می دهد متن را از فایل های PDF استخراج کنید.
دومین کلاس بتن EmlParser
است که از آن برای تجزیه متن از ایمیل ها استفاده خواهید کرد:
class EmlParser(InformalParserInterface):
"""Extract text from an email"""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides InformalParserInterface.load_data_source()"""
pass
def extract_text_from_email(self, full_file_path: str) -> dict:
"""A method defined only in EmlParser.
Does not override InformalParserInterface.extract_text()
"""
pass
پیاده سازی مشخص InformalParserInterface
اکنون به شما امکان می دهد متن را از فایل های ایمیل استخراج کنید.
تا کنون، شما دو پیاده سازی مشخص از InformalPythonInterface
را تعریف کرده اید. با این حال، توجه داشته باشید که EmlParser
نمی تواند .extract_text()
را به درستی تعریف کند. اگر بخواهید بررسی کنید که آیا EmlParser
InformalParserInterface
را پیاده سازی می کند یا خیر، نتیجه زیر را دریافت خواهید کرد:
>>> # Check if both PdfParser and EmlParser implement InformalParserInterface
>>> issubclass(PdfParser, InformalParserInterface)
True
>>> issubclass(EmlParser, InformalParserInterface)
True
این کار True
را برمی گرداند، که کمی مشکل ایجاد می کند زیرا تعریف یک رابط را نقض می کند!
اکنون ترتیب وضوح روش (MRO) PdfParser
و EmlParser
را بررسی کنید. این به شما سوپرکلاس های کلاس مورد نظر و همچنین ترتیبی را که برای اجرای یک متد جستجو می شوند، می گوید. شما می توانید MRO کلاس را با استفاده از روش dunder مشاهده کنید cls.__mro__
:
>>> PdfParser.__mro__
(__main__.PdfParser, __main__.InformalParserInterface, object)
>>> EmlParser.__mro__
(__main__.EmlParser, __main__.InformalParserInterface, object)
چنین رابط های غیررسمی برای پروژه های کوچکی که تنها تعداد کمی از توسعه دهندگان روی کد منبع کار می کنند، خوب است. با این حال، همانطور که پروژه ها بزرگتر می شوند و تیم ها رشد می کنند، این می تواند منجر به این شود که توسعه دهندگان ساعت های بی شماری را صرف جستجوی خطاهای منطقی سخت در پایگاه کد کنند!
استفاده از متاکلاس ها
در حالت ایده آل، شما می خواهید issubclass(EmlParser, InformalParserInterface)
زمانی که کلاس پیاده سازی همه متدهای انتزاعی رابط را تعریف نمی کند، False
را برگرداند. برای انجام این کار، یک متاکلاس به نام ParserMeta
ایجاد خواهید کرد. شما دو روش dunder را نادیده خواهید گرفت:
.__instancecheck__()
.__subclasscheck__()
در بلوک کد زیر، کلاسی به نام UpdatedInformalParserInterface
ایجاد می کنید که از متاکلاس ParserMeta
ساخته می شود:
class ParserMeta(type):
"""A Parser metaclass that will be used for parser class creation.
"""
def __instancecheck__(cls, instance):
return cls.__subclasscheck__(type(instance))
def __subclasscheck__(cls, subclass):
return (hasattr(subclass, 'load_data_source') and
callable(subclass.load_data_source) and
hasattr(subclass, 'extract_text') and
callable(subclass.extract_text))
class UpdatedInformalParserInterface(metaclass=ParserMeta):
"""This interface is used for concrete classes to inherit from.
There is no need to define the ParserMeta methods as any class
as they are implicitly made available via .__subclasscheck__().
"""
pass
اکنون که ParserMeta
و UpdatedInformalParserInterface
ایجاد شده اند، می توانید پیاده سازی های مشخص خود را ایجاد کنید.
ابتدا یک کلاس جدید برای تجزیه فایل های PDF به نام PdfParserNew
ایجاد کنید:
class PdfParserNew:
"""Extract text from a PDF."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides UpdatedInformalParserInterface.load_data_source()"""
pass
def extract_text(self, full_file_path: str) -> dict:
"""Overrides UpdatedInformalParserInterface.extract_text()"""
pass
در اینجا، PdfParserNew
.load_data_source()
و .extract_text()
را لغو می کند، بنابراین issubclass(PdfParserNew, UpdatedInformalParserInterface)
باید True
را برگرداند.
در این بلوک کد بعدی، شما یک پیاده سازی جدید از تجزیه کننده ایمیل به نام EmlParserNew
دارید:
class EmlParserNew:
"""Extract text from an email."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides UpdatedInformalParserInterface.load_data_source()"""
pass
def extract_text_from_email(self, full_file_path: str) -> dict:
"""A method defined only in EmlParser.
Does not override UpdatedInformalParserInterface.extract_text()
"""
pass
در اینجا، شما یک متا کلاس دارید که برای ایجاد UpdatedInformalParserInterface
استفاده می شود. با استفاده از یک متاکلاس، نیازی به تعریف صریح زیر کلاس ها ندارید. در عوض، زیر کلاس باید روش های مورد نیاز را تعریف کند. اگر اینطور نباشد، issubclass(EmlParserNew, UpdatedInformalParserInterface)
False
را برمی گرداند.
اجرای issubclass()
در کلاس های بتنی شما موارد زیر را تولید می کند:
>>> issubclass(PdfParserNew, UpdatedInformalParserInterface)
True
>>> issubclass(EmlParserNew, UpdatedInformalParserInterface)
False
همانطور که انتظار می رود، EmlParserNew
زیر کلاس UpdatedInformalParserInterface
نیست زیرا .extract_text()
در EmlParserNew
تعریف نشده است.
حالا بیایید نگاهی به MRO بیندازیم:
>>> PdfParserNew.__mro__
(<class '__main__.PdfParserNew'>, <class 'object'>)
همانطور که می بینید، UpdatedInformalParserInterface
یک سوپرکلاس از PdfParserNew
است، اما در MRO ظاهر نمی شود. این رفتار غیرمعمول ناشی از این واقعیت است که UpdatedInformalParserInterface
یک کلاس پایه مجازی از PdfParserNew
است.
استفاده از کلاس های پایه مجازی
در مثال قبلی، issubclass(EmlParserNew, UpdatedInformalParserInterface)
True
را برگرداند، حتی اگر UpdatedInformalParserInterface
در EmlParserNew
MRO ظاهر نشد. دلیلش این است که UpdatedInformalParserInterface
یک کلاس پایه مجازی از EmlParserNew
است.
تفاوت اصلی بین این زیر کلاس ها و زیر کلاس های استاندارد این است که کلاس های پایه مجازی از روش .__subclasscheck__()
dunder استفاده می کنند تا به طور ضمنی بررسی کنند که آیا یک کلاس یک زیر کلاس مجازی از سوپرکلاس است یا خیر. علاوه بر این، کلاس های پایه مجازی در زیر کلاس MRO ظاهر نمی شوند.
به این بلوک کد نگاهی بیندازید:
class PersonMeta(type):
"""A person metaclass"""
def __instancecheck__(cls, instance):
return cls.__subclasscheck__(type(instance))
def __subclasscheck__(cls, subclass):
return (hasattr(subclass, 'name') and
callable(subclass.name) and
hasattr(subclass, 'age') and
callable(subclass.age))
class PersonSuper:
"""A person superclass"""
def name(self) -> str:
pass
def age(self) -> int:
pass
class Person(metaclass=PersonMeta):
"""Person interface built from PersonMeta metaclass."""
pass
در اینجا، شما تنظیمات لازم برای ایجاد کلاس های پایه مجازی خود را دارید:
- متاکلاس
PersonMeta
- کلاس پایه
PersonSuper
شخص
رابط پایتون
اکنون که راه اندازی برای ایجاد کلاس های پایه مجازی انجام شده است، دو کلاس مشخص Employee
و Friend
را تعریف خواهید کرد. کلاس Employee
از PersonSuper
به ارث می برد، در حالی که Friend
به طور ضمنی از Person
به ارث می برد:
Inheriting subclasses
class Employee(PersonSuper):
"""Inherits from PersonSuper
PersonSuper will appear in Employee.__mro__
"""
pass
class Friend:
"""Built implicitly from Person
Friend is a virtual subclass of Person since
both required methods exist.
Person not in Friend.__mro__
"""
def name(self):
pass
def age(self):
pass
اگرچه Friend
به صراحت از Person
به ارث نمی برد، اما .name()
و .age()
را پیاده سازی می کند، بنابراین Person
به یک کلاس پایه مجازی از Friend
تبدیل می شود. هنگامی که issubclass(Friend, Person)
را اجرا می کنید، باید True
را برگرداند، به این معنی که Friend
زیر کلاس Person
است.
نمودار UML زیر نشان می دهد که وقتی issubclass()
را در کلاس Friend
فراخوانی می کنید چه اتفاقی می افتد:
با نگاهی به PersonMeta
، متوجه خواهید شد که متد dunder دیگری به نام .__instancecheck__()
وجود دارد. این روش برای بررسی اینکه آیا نمونه های Friend
از رابط Person
ایجاد شده اند یا خیر استفاده می شود. وقتی از isinstance(Friend, Person)
استفاده می کنید، کد شما .__instancecheck__()
را فراخوانی می کند.
رابط های رسمی
رابط های غیررسمی می توانند برای پروژه هایی با پایه کد کوچک و تعداد محدودی برنامه نویس مفید باشند. با این حال، رابط های غیررسمی رویکرد اشتباهی برای برنامه های بزرگتر خواهند بود. برای ایجاد یک رابط رسمی پایتون، به چند ابزار دیگر از ماژول abc
پایتون نیاز دارید.
با استفاده از abc. ABCMeta
برای اجرای نمونه سازی زیر کلاس متدهای انتزاعی، از ABCMeta
داخلی پایتون از ماژول abc
استفاده خواهید کرد. با بازگشت به رابط UpdatedInformalParserInterface
خود، متاکلاس خود را به نام ParserMeta
با متدهای dunder لغو شده .__instancecheck__()
و .__subclasscheck__()
ایجاد کردید.
به جای ایجاد متاکلاس خود، از abc استفاده خواهید کرد. ABCMeta
به عنوان متاکلاس. سپس، .__subclasshook__()
را به جای .__instancecheck__()
و .__subclasscheck__()
بازنویسی می کنید، زیرا پیاده سازی مطمئن تری از این متدهای dunder ایجاد می کند.
با استفاده از .__subclasshook__()
در اینجا پیاده سازی FormalParserInterface
با استفاده از abc آمده است. ABCMeta
به عنوان متاکلاس شما:
import abc
class FormalParserInterface(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (hasattr(subclass, 'load_data_source') and
callable(subclass.load_data_source) and
hasattr(subclass, 'extract_text') and
callable(subclass.extract_text))
class PdfParserNew:
"""Extract text from a PDF."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides FormalParserInterface.load_data_source()"""
pass
def extract_text(self, full_file_path: str) -> dict:
"""Overrides FormalParserInterface.extract_text()"""
pass
class EmlParserNew:
"""Extract text from an email."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides FormalParserInterface.load_data_source()"""
pass
def extract_text_from_email(self, full_file_path: str) -> dict:
"""A method defined only in EmlParser.
Does not override FormalParserInterface.extract_text()
"""
pass
اگر issubclass()
را روی PdfParserNew
و EmlParserNew
اجرا کنید، issubclass()
به ترتیب True
و False
را برمی گرداند.
استفاده از abc
برای رجیستر یک زیر کلاس مجازی
هنگامی که ماژول abc
را وارد کردید، می توانید مستقیما با استفاده از متروش .register()
یک زیر کلاس مجازی ثبت کنید. در مثال بعدی، رابط Double
را به عنوان یک کلاس پایه مجازی کلاس __float__
داخلی ثبت می کنید:
class Double(metaclass=abc.ABCMeta):
"""Double precision floating point number."""
pass
Double.register(float)
می توانید اثر استفاده از .register()
را بررسی کنید:
>>> issubclass(float, Double)
True
>>> isinstance(1.2345, Double)
True
با استفاده از متد متا .register()،
Double را با
موفقیت به عنوان یک زیر کلاس مجازی شناور
ثبت کرده اید.
هنگامی که Double
را ثبت کردید، می توانید از آن به عنوان دکوراتور کلاس برای تنظیم کلاس تزئین شده به عنوان یک زیر کلاس مجازی استفاده کنید:
@Double.register
class Double64:
"""A 64-bit double-precision floating-point number."""
pass
print(issubclass(Double64, Double)) # True
روش ثبت دکوراتور به شما کمک می کند تا سلسله مراتبی از وراثت کلاس مجازی سفارشی ایجاد کنید.
استفاده از تشخیص زیر کلاس با رجیستر
هنگام ترکیب .__subclasshook__()
با .register()
باید مراقب باشید، زیرا .__subclasshook__()
بر ثبت نام زیر کلاس مجازی اولویت دارد. برای اطمینان از اینکه زیر کلاس های مجازی ثبت شده در نظر گرفته می شوند، باید NotImplemented
را به متد .__subclasshook__()
dunder اضافه کنید. FormalParserInterface
به موارد زیر به روز می شود:
class FormalParserInterface(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (hasattr(subclass, 'load_data_source') and
callable(subclass.load_data_source) and
hasattr(subclass, 'extract_text') and
callable(subclass.extract_text) or
NotImplemented)
class PdfParserNew:
"""Extract text from a PDF."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides FormalParserInterface.load_data_source()"""
pass
def extract_text(self, full_file_path: str) -> dict:
"""Overrides FormalParserInterface.extract_text()"""
pass
@FormalParserInterface.register
class EmlParserNew:
"""Extract text from an email."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides FormalParserInterface.load_data_source()"""
pass
def extract_text_from_email(self, full_file_path: str) -> dict:
"""A method defined only in EmlParser.
Does not override FormalParserInterface.extract_text()
"""
pass
print(issubclass(PdfParserNew, FormalParserInterface)) # True
print(issubclass(EmlParserNew, FormalParserInterface)) # True
از آنجایی که از ثبت نام استفاده کرده اید، می توانید ببینید که EmlParserNew
یک زیر کلاس مجازی از رابط کاربری FormalParserInterface
شما در نظر گرفته شده است. این چیزی نیست که شما می خواستید زیرا EmlParserNew
.extract_text()
را لغو نمی کند. لطفا در ثبت نام زیر کلاس مجازی احتیاط کنید!
با استفاده از روش انتزاعی اعلامیه
متد انتزاعی روشی است که توسط رابط پایتون اعلام می شود، اما ممکن است پیاده سازی مفیدی نداشته باشد. روش انتزاعی باید توسط کلاس مشخصی که رابط مورد نظر را پیاده سازی می کند، لغو شود.
برای ایجاد متدهای انتزاعی در پایتون، دکوراتور @abc.abstractmethod
را به متدهای رابط اضافه می کنید. در مثال بعدی، FormalParserInterface
را به روز می کنید تا متدهای انتزاعی .load_data_source()
و .extract_text()
را شامل شود:
class FormalParserInterface(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (hasattr(subclass, 'load_data_source') and
callable(subclass.load_data_source) and
hasattr(subclass, 'extract_text') and
callable(subclass.extract_text) or
NotImplemented)
@abc.abstractmethod
def load_data_source(self, path: str, file_name: str):
"""Load in the data set"""
raise NotImplementedError
@abc.abstractmethod
def extract_text(self, full_file_path: str):
"""Extract text from the data set"""
raise NotImplementedError
class PdfParserNew(FormalParserInterface):
"""Extract text from a PDF."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides FormalParserInterface.load_data_source()"""
pass
def extract_text(self, full_file_path: str) -> dict:
"""Overrides FormalParserInterface.extract_text()"""
pass
class EmlParserNew(FormalParserInterface):
"""Extract text from an email."""
def load_data_source(self, path: str, file_name: str) -> str:
"""Overrides FormalParserInterface.load_data_source()"""
pass
def extract_text_from_email(self, full_file_path: str) -> dict:
"""A method defined only in EmlParser.
Does not override FormalParserInterface.extract_text()
"""
pass
در مثال بالا، شما در نهایت یک رابط رسمی ایجاد کرده اید که زمانی که روش های انتزاعی لغو نمی شوند، خطاها را افزایش می دهد. نمونه PdfParserNew
pdf_parser
هیچ خطایی ایجاد نمی کند، زیرا PdfParserNew
به درستی روش های انتزاعی FormalParserInterface
را نادیده می گیرد. با این حال, EmlParserNew
یک خطا را مطرح می کند:
>>> pdf_parser = PdfParserNew()
>>> eml_parser = EmlParserNew()
Traceback (most recent call last):
File "real_python_interfaces.py", line 53, in <module>
eml_interface = EmlParserNew()
TypeError: Can't instantiate abstract class EmlParserNew with abstract methods extract_text
همانطور که می بینید، پیام traceback به شما می گوید که شما تمام روش های انتزاعی را لغو نکرده اید. این رفتاری است که هنگام ساخت یک رابط رسمی پایتون انتظار دارید.
رابط ها به زبان های دیگر
رابط ها در بسیاری از زبان های برنامه نویسی ظاهر می شوند و پیاده سازی آنها از زبانی به زبان دیگر بسیار متفاوت است. در چند بخش بعدی، رابط های پایتون را با جاوا، C++ و Go مقایسه خواهید کرد.
جاوا
برخلاف پایتون، جاوا حاوی یک کلمه کلیدی رابط
است. با توجه به مثال تجزیه کننده فایل، یک رابط در جاوا را به این صورت اعلام می کنید:
public interface FileParserInterface {
// Static fields, and abstract methods go here ...
public void loadDataSource();
public void extractText();
}
اکنون دو کلاس بتنی PdfParser
و EmlParser
برای پیاده سازی FileParserInterface
ایجاد خواهید کرد. برای انجام این کار، باید از کلمه کلیدی implements
در تعریف کلاس استفاده کنید، مانند:
public class EmlParser implements FileParserInterface {
public void loadDataSource() {
// Code to load the data set
}
public void extractText() {
// Code to extract the text
}
}
در ادامه با مثال تجزیه فایل خود، یک رابط جاوا کاملا کاربردی چیزی شبیه به این خواهد بود:
import java.util.*;
import java.io.*;
public class FileParser {
public static void main(String[] args) throws IOException {
// The main entry point
}
public interface FileParserInterface {
HashMap<String, ArrayList<String>> file_contents = null;
public void loadDataSource();
public void extractText();
}
public class PdfParser implements FileParserInterface {
public void loadDataSource() {
// Code to load the data set
}
public void extractText() {
// Code to extract the text
}
}
public class EmlParser implements FileParserInterface {
public void loadDataSource() {
// Code to load the data set
}
public void extractText() {
// Code to extract the text
}
}
}
همانطور که می بینید، یک رابط پایتون در طول ایجاد انعطاف پذیری بسیار بیشتری نسبت به رابط جاوا به شما می دهد.
C
مانند پایتون، C++ از کلاس های پایه انتزاعی برای ایجاد رابط ها استفاده می کند. هنگام تعریف یک رابط در C++، از کلمه کلیدی virtual
برای توصیف متدی استفاده می کنید که باید در کلاس بتن بازنویسی شود:
class FileParserInterface {
public:
virtual void loadDataSource(std::string path, std::string file_name);
virtual void extractText(std::string full_file_name);
};
هنگامی که می خواهید رابط را پیاده سازی کنید، نام کلاس بتن و به دنبال آن یک دونقطه (:
) و سپس نام رابط را می دهید. مثال زیر پیاده سازی رابط C++ را نشان می دهد:
class PdfParser : FileParserInterface {
public:
void loadDataSource(std::string path, std::string file_name);
void extractText(std::string full_file_name);
};
class EmlParser : FileParserInterface {
public:
void loadDataSource(std::string path, std::string file_name);
void extractText(std::string full_file_name);
};
یک رابط پایتون و یک رابط C++ شباهت هایی دارند زیرا هر دو از کلاس های پایه انتزاعی برای شبیه سازی رابط ها استفاده می کنند.
رفتن
اگرچه نحو Go یادآور پایتون است، اما زبان برنامه نویسی Go شامل یک کلمه کلیدی رابط
مانند جاوا است. بیایید fileParserInterface
را در Go ایجاد کنیم:
type fileParserInterface interface {
loadDataSet(path string, filename string)
extractText(full_file_path string)
}
تفاوت بزرگ بین پایتون و Go این است که Go کلاس ندارد. در عوض، Go از این نظر شبیه به C است که از کلمه کلیدی struct
برای ایجاد ساختارها استفاده می کند. یک ساختار شبیه به یک کلاس است که یک ساختار حاوی داده ها و روش ها است. با این حال، برخلاف یک کلاس، همه داده ها و روش ها به صورت عمومی قابل دسترسی هستند. ساختارهای بتنی در Go برای پیاده سازی fileParserInterface
استفاده می شود.
در اینجا مثالی از نحوه استفاده Go از رابط ها آورده شده است:
package main
type fileParserInterface interface {
loadDataSet(path string, filename string)
extractText(full_file_path string)
}
type pdfParser struct {
// Data goes here ...
}
type emlParser struct {
// Data goes here ...
}
func (p pdfParser) loadDataSet() {
// Method definition ...
}
func (p pdfParser) extractText() {
// Method definition ...
}
func (e emlParser) loadDataSet() {
// Method definition ...
}
func (e emlParser) extractText() {
// Method definition ...
}
func main() {
// Main entrypoint
}
برخلاف رابط پایتون، یک رابط Go با استفاده از ساختارها و رابط
کلمه کلیدی صریح ایجاد می شود.
نتیجه
پایتون هنگام ایجاد رابط ها انعطاف پذیری زیادی را ارائه می دهد. یک رابط غیررسمی پایتون برای پروژه های کوچک مفید است که در آن کمتر در مورد انواع بازگشتی متدها سردرگم می شوید. با رشد یک پروژه، نیاز به یک رابط رسمی پایتون اهمیت بیشتری پیدا می کند زیرا استنباط انواع بازگشت دشوارتر می شود. این تضمین می کند که کلاس بتن، که رابط را پیاده سازی می کند، روش های انتزاعی را بازنویسی می کند.
اکنون می توانید:
- آشنایی با نحوه عملکرد رابط ها و هشدارهای ایجاد رابط پایتون
- آشنایی با سودمندی رابط ها در یک زبان پویا مانند پایتون
- پیاده سازی رابط های رسمی و غیررسمی در پایتون
- رابط های پایتون را با رابط های زبان هایی مانند Java، C++ و Go مقایسه کنید.
اکنون که با نحوه ایجاد رابط پایتون آشنا شدید، یک رابط پایتون به پروژه بعدی خود اضافه کنید تا سودمندی آن را در عمل ببینید!