پیاده سازی یک رابط در پایتون


رابط ها نقش مهمی در مهندسی نرم افزار دارند. با رشد یک برنامه، مدیریت به روزرسانی ها و تغییرات در پایگاه کد دشوارتر می شود. بیشتر اوقات، کلاس هایی دارید که بسیار شبیه به هم هستند اما نامرتبط هستند، که می تواند منجر به سردرگمی شود. در این آموزش، خواهید دید که چگونه می توانید از یک رابط پایتون برای کمک به تعیین کلاس برای مقابله با مشکل فعلی استفاده کنید.

در این آموزش می توانید:

  • آشنایی با نحوه عملکرد رابط ها و هشدارهای ایجاد رابط پایتون
  • درک کنید که رابط ها در یک زبان پویا مانند پایتون چقدر مفید هستند.
  • پیاده سازی یک رابط غیررسمی پایتون
  • از 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 با یک کلاس استاندارد پایتون یکسان به نظر می رسد. شما به تایپ اردک تکیه می کنید تا به کاربران اطلاع دهید که این یک رابط کاربری است و باید بر اساس آن استفاده شود.

با در نظر گرفتن تایپ اردک، شما دو کلاس را تعریف می کنید که 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

در اینجا، شما تنظیمات لازم برای ایجاد کلاس های پایه مجازی خود را دارید:

  1. متاکلاس PersonMeta
  2. کلاس پایه PersonSuper
  3. شخص رابط پایتون

اکنون که راه اندازی برای ایجاد کلاس های پایه مجازی انجام شده است، دو کلاس مشخص 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 مقایسه کنید.

اکنون که با نحوه ایجاد رابط پایتون آشنا شدید، یک رابط پایتون به پروژه بعدی خود اضافه کنید تا سودمندی آن را در عمل ببینید!