abc --- کلاس‌های پایه انتزاعی

کد منبع: Lib/abc.py


این ماژول زیرساخت لازم برای تعریف کلاس‌های پایه انتزاعی (ABCها) در پایتون را فراهم می‌کند، همان‌طور که در PEP 3119 شرح داده شده است؛ برای آگاهی از دلیل افزودن این قابلیت به پایتون، این PEP را ببینید. (همچنین PEP 3141 و ماژول numbers را در مورد سلسله‌مراتب نوع برای اعداد بر پایه ABCها ببینید.)

ماژول collections تعدادی کلاس ملموس دارد که از ABCها مشتق شده‌اند؛ البته می‌توان این‌ها را نیز بیش‌تر مشتق کرد. علاوه بر این، زیرماژول collections.abc تعدادی ABC دارد که می‌توان از آن‌ها برای بررسی اینکه آیا یک کلاس یا نمونه، رابط خاصی را فراهم می‌کند، استفاده کرد؛ برای مثال، اگر hashable باشد یا یک mapping باشد.

این ماژول، فراکلاس ABCMeta را برای تعریف ABCها و کلاس کمکی ABC را فراهم می‌کند تا به‌عنوان جایگزین، بتوانید ABCها را از طریق ارث‌بری تعریف کنید:

class abc.ABC

یک کلاس کمکی که ABCMeta را به‌عنوان فراکلاس خود دارد. با این کلاس، می‌توان یک کلاس پایه انتزاعی را صرفاً با مشتق شدن از ABC ایجاد کرد و از کاربرد گاه گیج‌کننده‌ی فراکلاس اجتناب نمود، برای مثال:

from abc import ABC

class MyABC(ABC):
    pass

توجه داشته باشید که نوع ABC همچنان ABCMeta است؛ بنابراین ارث‌بری از ABC نیازمند رعایت نکات احتیاطی معمول در مورد استفاده از فراکلاس است، زیرا ارث‌بری چندگانه ممکن است به تعارض‌های فراکلاس منجر شود. همچنین می‌توان یک کلاس پایه انتزاعی را با ارسال کلیدواژه metaclass و استفاده مستقیم از ABCMeta تعریف کرد، برای مثال:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

اضافه شده در نسخه‌ی 3.4.

class abc.ABCMeta

فراکلاس برای تعریف کلاس‌های پایه انتزاعی (ABCها).

از این فراکلاس برای ایجاد یک ABC استفاده کنید. یک ABC به‌طور مستقیم قابل زیرکلاس‌بندی است و سپس به‌عنوان یک کلاس میکس‌این عمل می‌کند. همچنین می‌توانید کلاس‌های عینی غیرمرتبط (حتی کلاس‌های توکار) و ABCهای غیرمرتبط را به‌عنوان «زیرکلاس‌های مجازی» ثبت کنید — این کلاس‌ها و زیرکلاس‌های مشتق‌شده از آن‌ها از دید تابع توکار issubclass() زیرکلاس‌های ABC ثبت‌کننده محسوب خواهند شد، اما ABC ثبت‌کننده در MRO (ترتیب حل متد) آن‌ها ظاهر نخواهد شد و پیاده‌سازی‌های متد تعریف‌شده توسط ABC ثبت‌کننده فراخوانی‌پذیر نخواهند بود (حتی از طریق super() نیز). [1]

کلاس‌هایی که با فراکلاس ABCMeta ایجاد شده‌اند، متد زیر را دارند:

register(subclass)

subclass را به‌عنوان یک «زیرکلاس مجازی» از این ABC ثبت کنید. برای مثال:

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

تغییر یافته در نسخه‌ی 3.3: زیرکلاس ثبت‌شده را برمی‌گرداند تا امکان استفاده به‌عنوان دکوراتور کلاس فراهم شود.

تغییر یافته در نسخه‌ی 3.4: برای تشخیص فراخوانی‌های register()، می‌توانید از تابع get_cache_token() استفاده کنید.

همچنین می‌توانید این متد را در یک کلاس پایه انتزاعی بازنویسی کنید:

__subclasshook__(subclass)

(باید به‌عنوان متد کلاس تعریف شود.)

بررسی می‌کند که آیا subclass زیرکلاسی از این ABC در نظر گرفته می‌شود یا خیر. این بدان معناست که شما می‌توانید رفتار issubclass() را بیشتر سفارشی‌سازی کنید، بدون نیاز به فراخوانی register() برای هر کلاسی که می‌خواهید آن را زیرکلاسی از ABC در نظر بگیرید. (این متد کلاس از متد __subclasscheck__() در ABC فراخوانی می‌شود.)

این متد باید True، False یا NotImplemented را برگرداند. اگر True را برگرداند، زیرکلاس به‌عنوان زیرکلاسی از این ABC در نظر گرفته می‌شود. اگر False را برگرداند، زیرکلاس به‌عنوان زیرکلاسی از این ABC در نظر گرفته نمی‌شود، حتی اگر در حالت عادی یک زیرکلاس باشد. اگر NotImplemented را برگرداند، بررسی زیرکلاس با سازوکار معمول ادامه می‌یابد.

برای نمایش این مفاهیم، این نمونه تعریف ABC را ببینید:

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

MyIterable.register(Foo)

کلاس پایه انتزاعی (ABC) MyIterable متد استاندارد پیمایش‌پذیر، __iter__()، را به‌عنوان یک متد انتزاعی تعریف می‌کند. پیاده‌سازی ارائه‌شده در اینجا همچنان می‌تواند از زیرکلاس‌ها فراخوانی شود. متد get_iterator() نیز بخشی از کلاس پایه انتزاعی MyIterable است، اما نیازی به بازنویسی آن در کلاس‌های مشتق‌شده غیرانتزاعی نیست.

متد کلاسی __subclasshook__() تعریف‌شده در اینجا بیان می‌کند که هر کلاسی که یک متد __iter__() در __dict__ خود (یا در دیکشنری یکی از کلاس‌های پایه‌ی خود، که از طریق فهرست __mro__ به آن‌ها دسترسی پیدا می‌شود) داشته باشد، به‌عنوان یک MyIterable نیز در نظر گرفته می‌شود.

سرانجام، آخرین خط Foo را به یک زیرکلاس مجازی از MyIterable تبدیل می‌کند، اگرچه متد __iter__() را تعریف نمی‌کند (از پروتکل پیمایش‌پذیر به‌سبک قدیمی استفاده می‌کند که بر حسب __len__() و __getitem__() تعریف شده است). توجه داشته باشید که این کار get_iterator را به‌عنوان متدی از Foo در دسترس قرار نمی‌دهد، بنابراین به‌صورت جداگانه ارائه شده است.

ماژول abc همچنین دکوراتور زیر را نیز فراهم می‌کند:

@abc.abstractmethod

دکوراتوری که متدهای انتزاعی را نشان می‌دهد.

استفاده از این دکوراتور مستلزم آن است که فراکلاسِ کلاس ABCMeta باشد یا از آن مشتق شده باشد. کلاسی که فراکلاس آن از ABCMeta مشتق شده باشد، نمی‌تواند نمونه‌سازی شود، مگر آنکه تمام متدها و ویژگی‌های انتزاعی آن بازنویسی شده باشند. می‌توان متدهای انتزاعی را با استفاده از هر یک از سازوکارهای معمول فراخوانی 'super' فراخوانی کرد. می‌توان از @abstractmethod برای اعلام متدهای انتزاعی برای ویژگی‌ها و توصیف‌گرها استفاده کرد.

افزودن متدهای انتزاعی به‌صورت پویا به یک کلاس، یا تلاش برای تغییر وضعیت انتزاعی یک متد یا کلاس پس از ایجاد آن، تنها با استفاده از تابع update_abstractmethods() پشتیبانی می‌شود. @abstractmethod تنها بر زیرکلاس‌های مشتق‌شده با وراثت معمولی تأثیر می‌گذارد؛ «زیرکلاس‌های مجازی» ثبت‌شده با متد register() مربوط به ABC تحت تأثیر قرار نمی‌گیرند.

هنگامی که @abstractmethod در ترکیب با سایر توصیف‌گرهای متد اعمال می‌شود، باید به‌عنوان درونی‌ترین دکوراتور اعمال شود، همان‌طور که در مثال‌های استفاده زیر نشان داده شده است:

class C(ABC):
    @abstractmethod
    def my_abstract_method(self, arg1):
        ...
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg2):
        ...
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg3):
        ...

    @property
    @abstractmethod
    def my_abstract_property(self):
        ...
    @my_abstract_property.setter
    @abstractmethod
    def my_abstract_property(self, val):
        ...

    @abstractmethod
    def _get_x(self):
        ...
    @abstractmethod
    def _set_x(self, val):
        ...
    x = property(_get_x, _set_x)

برای تعامل صحیح با سازوکار کلاس پایه انتزاعی، توصیف‌گر باید خود را با استفاده از __isabstractmethod__ به‌عنوان انتزاعی معرفی کند. به‌طور کلی، اگر هر یک از متدهای استفاده‌شده برای تشکیل توصیف‌گر انتزاعی باشند، این ویژگی باید True باشد. برای مثال، @property توکار پایتون معادل زیر را انجام می‌دهد:

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for
                   f in (self._fget, self._fset, self._fdel))

توجه

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

ماژول abc همچنین از دکوراتورهای قدیمی زیر پشتیبانی می‌کند:

@abc.abstractclassmethod

اضافه شده در نسخه‌ی 3.2.

منسوخ شده از نسخه‌ی 3.3: اکنون می‌توان از @classmethod همراه با @abstractmethod استفاده کرد، که این دکوراتور را زائد می‌سازد.

زیرکلاسی از classmethod توکار، که نشان‌دهنده‌ی یک classmethod انتزاعی است. در سایر موارد، مشابه @abstractmethod است.

این حالت خاص منسوخ شده است، زیرا دکوراتور @classmethod اکنون هنگامی که به یک متد انتزاعی اعمال شود، به‌درستی به‌عنوان انتزاعی شناسایی می‌شود:

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg):
        ...
@abc.abstractstaticmethod

اضافه شده در نسخه‌ی 3.2.

منسوخ شده از نسخه‌ی 3.3: اکنون می‌توانید از @staticmethod همراه با @abstractmethod استفاده کنید، که این دکوراتور را زائد می‌کند.

زیرکلاسی از staticmethod توکار که نشان‌دهنده یک staticmethod انتزاعی است. در غیر این صورت، مشابه @abstractmethod است.

این حالت خاص منسوخ شده است، زیرا دکوراتور @staticmethod اکنون هنگامی که به یک متد انتزاعی اعمال شود، به‌درستی به‌عنوان انتزاعی شناسایی می‌شود:

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg):
        ...
@abc.abstractproperty

منسوخ شده از نسخه‌ی 3.3: اکنون می‌توان از @property، @property.getter، @property.setter و @property.deleter به‌همراه @abstractmethod استفاده کرد، که این دکوراتور را زائد می‌کند.

زیرکلاسی از property توکار، که نشان‌دهنده‌ی یک ویژگی انتزاعی است.

این حالت خاص منسوخ شده است، زیرا دکوراتور @property اکنون هنگام اعمال بر یک متد انتزاعی به‌درستی به‌عنوان انتزاعی شناسایی می‌شود:

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

مثال بالا یک ویژگی فقط‌خواندنی تعریف می‌کند؛ شما همچنین می‌توانید با علامت‌گذاری مناسب یک یا چند مورد از متدهای زیربنایی به‌عنوان انتزاعی، یک ویژگی انتزاعی با قابلیت خواندن و نوشتن تعریف کنید:

class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

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

class D(C):
    @C.x.setter
    def x(self, val):
        ...

ماژول abc همچنین توابع زیر را فراهم می‌کند:

abc.get_cache_token()

توکن فعلی نهانگاه کلاس پایه انتزاعی را بازمی‌گرداند.

توکن یک شیء غیرشفاف است (که از آزمون برابری پشتیبانی می‌کند) و نسخه کنونی نهانگاه کلاس پایه انتزاعی برای زیرکلاس‌های مجازی را شناسایی می‌کند. توکن با هر فراخوانی ABCMeta.register() روی هر ABC تغییر می‌کند.

اضافه شده در نسخه‌ی 3.4.

abc.update_abstractmethods(cls)

تابعی برای محاسبه‌ی مجدد وضعیت انتزاعی بودن یک کلاس انتزاعی. اگر متدهای انتزاعی یک کلاس پس از ایجاد آن پیاده‌سازی یا تغییر کرده باشند، باید این تابع فراخوانی شود. معمولاً این تابع باید از درون یک دکوراتور کلاس فراخوانی شود.

cls را برمی‌گرداند تا امکان استفاده به‌عنوان دکوراتور کلاس فراهم شود.

اگر cls نمونه‌ای از ABCMeta نباشد، هیچ کاری انجام نمی‌دهد.

توجه

این تابع فرض می‌کند که ابرکلاس‌های cls از قبل به‌روزرسانی شده‌اند. این تابع هیچ زیرکلاسی را به‌روزرسانی نمی‌کند.

اضافه شده در نسخه‌ی 3.10.

پانویس‌ها