ویژگی تصویر

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

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

کتابخانه typing در پایتون ابزارهایی برای افزودن تایپ هینت (type hints) به کد فراهم می‌کند. این تایپ‌ها به‌طور عمده برای خوانایی، نگهداری و تحلیل استاتیک کد (با ابزارهایی مثل mypy, pyright) کاربرد دارند و به اجرای برنامه در زمان اجرا تاثیری جزئی یا هیچ ندارند. در ادامه مفاهیم پایه، انواع پرکاربرد و نمونه‌های حرفه‌ای را بررسی می‌کنیم.

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

  • افزایش خوانایی و مستندسازی توابع و کلاس‌ها
  • کاهش باگ با تحلیل استاتیک کد
  • کمک به ابزارهای اتوماسیون، IDE و refactoring
  • تعریف API روشن برای تیم‌های بزرگ

انواع پایه و نحوه استفاده

نمونه ساده استفاده از Optional و Union:

from typing import Optional, Union

def parse_int(s: str) -> Optional[int]:
    try:
        return int(s)
    except ValueError:
        return None

def describe(x: Union[int, str]) -> str:
    return f"value: {x}"

در این مثال Optional[int] معادل Union[int, None] است. تایپ‌ها صرفاً برای مستندسازی و ابزارهای تحلیل استفاده می‌شوند و پایتون اجرا را تغییر نمی‌دهد.

Genericها و TypeVar

برای نوشتن توابع یا کلاس‌های جنریک از TypeVar استفاده می‌کنیم تا رفتار عمومی‌تری داشته باشیم:

from typing import TypeVar, List

T = TypeVar('T')

def first_element(items: List[T]) -> T:
    return items[0]

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

Callable — تایپ برای توابع به‌عنوان مقدار

اگر قرار است تابعی به‌عنوان آرگومان منتقل شود، از Callable استفاده می‌کنیم:

from typing import Callable

def apply_twice(f: Callable[[int], int], x: int) -> int:
    return f(f(x))

def inc(n: int) -> int:
    return n + 1

res = apply_twice(inc, 3)  # 5

در Callable[[int], int] لیست اول پارامترها و عنصر دوم نوع بازگشتی را مشخص می‌کند.

پشتیبانی از ساختارهای دلخواه: Protocol و TypedDict

گاه می‌خواهیم شیئی را بر اساس رفتار (مجموعه‌ای از متدها/ویژگی‌ها) تایپ کنیم، نه کلاس مشخص. اینجا Protocol مفید است:

from typing import Protocol

class HasClose(Protocol):
    def close(self) -> None: ...

def cleanup(resource: HasClose) -> None:
    resource.close()

هر شیئی که متد close داشته باشد با این پروتکل سازگار است، حتی اگر از یک کلاس مشترک ارث‌بری نکرده باشد.

برای ساختارهای دیکشنری-مانند با فیلدهای مشخص، از TypedDict استفاده می‌شود:

from typing import TypedDict

class Point(TypedDict):
    x: int
    y: int

p: Point = {"x": 1, "y": 2}

این نوع برای APIهایی که داده‌های JSON-like دریافت یا برمی‌گردانند بسیار مناسب است.

Literal، Final و Annotated

چند ابزار پیشرفته‌تر برای محدود کردن مقادیر یا افزودن متادیتا:

نوعکاربرد
Literalمحدود کردن مقدار به ثابت‌های مشخص (مفید برای حالت‌های enum-like)
Finalعلامت‌گذاری ثابت‌ها یا متدهایی که نباید override شوند
Annotatedافزودن متادیتا به تایپ (مثلاً برای فریمورک‌های validation)
from typing import Literal, Final, Annotated

Mode = Literal['r', 'w', 'a']
MAX_RETRIES: Final[int] = 5
UserId = Annotated[int, "database primary key"]

Mode تنها یکی از سه مقدار مشخص را می‌پذیرد و MAX_RETRIES به عنوان ثابت نشان داده می‌شود.

پشتیبانی از نسخه‌های جدید پایتون

از پایتون 3.9 به بعد می‌توان از سینتکس بومی برای جنریک‌ها استفاده کرد (مثلاً list[int] به‌جای List[int]). همچنین PEP 604 امکان نوشتن union ها به شکل int | str را فراهم کرد؛ با این حال کتابخانه typing هنوز برای انواع خاص مفید است.

مثال ترکیبی: API کوچک با تایپ‌ها

from typing import TypedDict, Optional, List

class User(TypedDict):
    id: int
    name: str
    email: Optional[str]

users: List[User] = []

def find_user(uid: int) -> Optional[User]:
    for u in users:
        if u['id'] == uid:
            return u
    return None

در این API ساده، User ساختار ثابت یک کاربر را بیان می‌کند و find_user یا یک کاربر یا None برمی‌گرداند. این تعریف برای تعیین قراردادهای ورودی/خروجی بسیار مناسب است.

Runtime vs Static — محدودیت‌ها و نکات عملیاتی

  • بسیاری از تایپ‌ها فقط در زمان تحلیل استاتیک معنا دارند و اجرا را تغییر نمی‌دهند.
  • برای بررسی نوع در زمان اجرا می‌توان از isinstance یا از typing.get_type_hints و کتابخانه‌هایی مثل pydantic یا typeguard استفاده کرد.
  • برای سازگاری با نسخه‌های قدیمی‌تر از typing_extensions بهره می‌برند.

نمونه: استفاده از get_type_hints و cast

from typing import get_type_hints, cast

def foo(x: int) -> str:
    return str(x)

hints = get_type_hints(foo)
# {'x': , 'return': }

value = cast(str, 123)  # به ابزارهای تایپ می‌گوید مقدار از نوع str است

get_type_hints هینت‌های تایپ تابع را بازمی‌گرداند و cast فقط برای تحلیل‌کننده‌هاست و در زمان اجرا تبدیل واقعی انجام نمی‌دهد.

نکات حرفه‌ای و بهترین روش‌ها

  • در پروژه‌های بزرگ از mypy یا pyright برای تحلیل مداوم استفاده کنید.
  • در مستندات و docstringها به تایپ‌ها اشاره کنید و APIها را واضح نگه دارید.
  • از TypedDict و Protocol برای تعریف قراردادهای آزادتر و قابل آزمون استفاده کنید.
  • سعی کنید تایپ‌ها را همگام با تغییرات کد به‌روز نگه دارید تا اعتبار آن‌ها حفظ شود.
  • برای پکت‌بندی سریع، از سینتکس جدید (list[int], int | str) استفاده کنید، اما با توجه به پشتیبانی نسخه‌ها مراقب سازگاری باشید.

جمع‌بندی

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

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

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