ویژگی تصویر

آشنایی با کتابخانه abc در پایتون

  /  پایتون   /  کتابخانه abc در پایتون
بنر تبلیغاتی الف

کتابخانهٔ استاندارد abc (Abstract Base Classes) یکی از ابزارهای مهم برای تعریف رابط‌ها (interfaces) و کلاس‌های انتزاعی در پایتون است. این ماژول کمک می‌کند قراردادهای طراحی (مثل وجود متدها و propertyها) را به‌صورت رسمی‌تر و قابل بررسی در زمان اجرا اعمال کنیم، بدون آن‌که الزاما پیاده‌سازی را در کلاس پایه فراهم کنیم.

چرا از abc استفاده کنیم؟

  • ایجاد قراردادهای واضح بین اجزاء یک سیستم (مثلاً سرویس‌ها، درایورها و adapterها).
  • جلوگیری از ایجاد نمونه از کلاس‌هایی که پیاده‌سازی کامل ندارند (TypeError در زمان ساخت شی).
  • امکان رجیستر کردن کلاس‌ها به‌عنوان زیرکلاس‌های مجازی با متد register.
  • قابلیت ترکیب با سیستم type hints و ابزارهای تحلیل استاتیک برای کد خواناتر و قابل نگهداری‌تر.

مفاهیم اصلی

  • ABC: کلاس پایهٔ انتزاعی (معمولا از abc.ABC ارث می‌برد).
  • @abstractmethod: دکوراتوری برای علامت‌گذاری متدهایی که باید در زیرکلاس‌ها پیاده‌سازی شوند.
  • ثبت مجازی (virtual subclass): با register می‌توان بدون ارث‌بری، کلاس را به‌عنوان زیرکلاس شناخت.

مثال پایه: تعریف یک رابط ساده

from abc import ABC, abstractmethod

class Storage(ABC):
    @abstractmethod
    def save(self, data):
        pass

    @abstractmethod
    def load(self, key):
        pass

در این مثال Storage یک کلاس انتزاعی است که دو متد انتزاعی تعریف کرده. اگر کلاس دیگری این دو متد را پیاده‌سازی نکند، نمی‌توان از آن نمونه‌سازی کرد.

مثال پیاده‌سازی و خطای رایج

class MemoryStorage(Storage):
    def save(self, data):
        self._data = data
# باز هم باید load را پیاده‌سازی کنیم؛ در غیر این صورت ساخت نمونه خطا می‌دهد

# این خط منجر به TypeError می‌شود:
# TypeError: Can't instantiate abstract class MemoryStorage with abstract methods load

خط بالا نشان می‌دهد که فراموش کردن پیاده‌سازی یکی از متدهای انتزاعی منجر به خطای زمان اجرا می‌شود.

پیاده‌سازی کامل

class MemoryStorage(Storage):
    def save(self, data):
        self._data = data

    def load(self, key):
        return getattr(self, '_data', None)

m = MemoryStorage()  # حالا قابل ساخت است
m.save("hello")
print(m.load(None))  # خروجی: "hello"

در این کد، MemoryStorage هر دو متد مورد نیاز را پیاده‌سازی می‌کند و بنابراین امکان نمونه‌سازی فراهم می‌شود.

ویژگی‌های پیشرفته: متد انتزاعی با پیاده‌سازی پیش‌فرض

class Base(ABC):
    @abstractmethod
    def process(self, x):
        print("default processing", x)

class Child(Base):
    def process(self, x):
        super().process(x)  # استفاده از پیاده‌سازی پیش‌فرض
        return x * 2

در پایتون می‌توان متدی را همزمان @abstractmethod علامت زد و پیاده‌سازی پیش‌فرض ارائه داد. زیرکلاس‌ها موظف به تعریف آن متدند اما می‌توانند از پیاده‌سازی پایه هم استفاده کنند.

خصوصیات (property) انتزاعی

from abc import ABC, abstractmethod

class Plugin(ABC):
    @property
    @abstractmethod
    def name(self):
        ...

برای تعریف property انتزاعی باید ترتیب دکوراتورها رعایت شود: ابتدا @property سپس @abstractmethod. زیرکلاس باید یک property با همین نام فراهم کند.

ثبت کلاس به‌عنوان زیرکلاس مجازی (register)

class LegacyStorage:
    def save(self, data): ...
    def load(self, key): ...

Storage.register(LegacyStorage)

print(issubclass(LegacyStorage, Storage))  # True
print(isinstance(LegacyStorage(), Storage))  # True

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

یکپارچگی با collections.abc و مثال کاربردی

ماژول collections.abc مجموعه‌ای از ABCهای مفید مثل Iterable، Sequence و Mapping فراهم می‌کند. برای مثال اگر می‌خواهید کلاس شما نقش یک نگهدارندهٔ iterable را ایفا کند، ارث‌بری از collections.abc.Iterable یا پیاده‌سازی روش‌های لازم کمک می‌کند.

from collections.abc import Iterable

class MyRange:
    def __init__(self, n):
        self.n = n
    def __iter__(self):
        for i in range(self.n):
            yield i

print(isinstance(MyRange(3), Iterable))  # True

در این مثال، پیاده‌سازی __iter__ کافی است تا شئ به‌عنوان Iterable شناخته شود.

مزایا و محدودیت‌ها

  • مزایا: افزایش خوانایی، تضمین قراردادها در زمان اجرا، و یکپارچگی با ابزارهای تحلیل.
  • محدودیت‌ها: هزینهٔ اندک در زمان اجرا برای بررسی isinstance و issubclass، و گاهی پیچیدگی در ترکیب با چندارث‌بری.
  • جایگزین‌های ساختاری: در Python 3.8+ می‌توان از typing.Protocol برای تایپ استراکچرال (structural typing) استفاده کرد که در زمان کامپایل استاتیک مفید است.

نکات فنی و نکات طراحی

  • اگر هدف فقط بررسی وجود متدهاست، اغلب duck typing ساده‌تر و سبک‌تر است؛ اما برای طراحی API عمومی و کتابخانه‌ها، ABCها مفیدتر هستند.
  • در کتابخانه‌های عمومی، документ‌نویسی دقیق دربارهٔ قراردادها همراه با ABC باعث می‌شود کاربران جایگزین‌های سفارشی بسازند.
  • در تست‌ها می‌توان از کلاس‌های شبیه‌سازی‌شده (mocks) استفاده کرد، اما ABCها کمک می‌کنند تست‌ها روی قرارداد و نه فقط روی پیاده‌سازی تمرکز کنند.

جدول مرجع سریع

دکوراتور / تابعکاربرد
@abstractmethodعلامت‌گذاری متدهای انتزاعی
@property + @abstractmethodتعریف خصوصیات انتزاعی
ABCکلاس پایهٔ انتزاعی (معمولا ارث‌بری از abc.ABC)
register()ثبت کلاس به‌عنوان زیرکلاس مجازی

جمع‌بندی و توصیه‌های عملی

کتابخانهٔ abc ابزار قدرتمندی برای ساخت رابط‌های واضح و امن در پایتون است. برای کتابخانه‌ها و پروژه‌های بزرگ که سازگاری و قراردادها اهمیت دارند، استفاده از ABC باعث کاهش خطا و افزایش قابل‌فهمی کد می‌شود. اما در کدهای کوچک یا اسکریپت‌های ساده، duck typing معمولی اغلب کفایت می‌کند. همچنین در مواقعی که می‌خواهید تایپ ساختاری را صریح کنید، ترکیب ABC یا typing.Protocol می‌تواند مفید باشد.

در نهایت: از ABCها برای تعریف قراردادها استفاده کنید، از register برای یکپارچه‌سازی با کلاس‌های قدیمی بهره ببرید، و همیشه مستندات و مثال‌های روشن برای کاربران (و خودتان) فراهم کنید تا پیاده‌سازی صحیح زیرکلاس‌ها ساده و بدون خطا باشد.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: