تایپهینتها به ما کمک میکنند تا نوع ورودی و خروجی توابع و متغیرها را مشخص کنیم. بدین ترتیب، میتوانیم پیش از اجرای کد، بسیاری از خطاهای نوعی را شناسایی کنیم، خوانایی کد را افزایش دهیم، و تجربهی توسعه را با اتوکامپلیت و تشخیص خطا در ویرایشگر بهبود ببخشیم.
چالش: اگر تایپهینت نبود چه میشد؟
فرض کنید تابعی برای محاسبه تخفیف داریم:
def calc_discount(price, discount):
return price * (1 - discount / 100)و در جایی از پروژه، این تابع را به صورت زیر فراخوانی کرده ایم:
result = calc_discount("100000", "10")ورودی های این تابع باید عدد باشد اما ما در اینجا رشته به آن ارسال کردهایم. پایتون هیچ خطایی نمیدهد، بنابراین تابع اجرا میشود و تازه در میانهی کار وقتی میخواهد مبلغ را محاسبه کند، برنامه از کار میافتد.
اکنون میتوانیم با افزودن تایپ هینت، کد را به شکل زیر بهبود دهیم.
def calc_discount(price: int, discount: int) -> float:
return price * (1 - discount / 100)اکنون (اگر تنظیمات مناسب روی ویرایشگر کد شما انجام شده باشد) ویرایشگر بلافاصله زیر process_payment("100000", "user_123") خط قرمز میکشد زیرا تایپهینت ها به وضوح میگویند که ورودی باید عدد صحیح باشد. در نتیجه مشکل پیش از رسیدن کد به پروژه اصلی و محیط پروداکشن آشکار میشود.
تایپهینت در پایتون یعنی چه
ارور های مربوط به تایپهینت مانع از اجرای کد نمیشوند. کد زیر با اینکه از نظر تایپ مشکل دارد، اما اجرا میشود.
def add(x: int, y: int) -> int:
return x + y
print(add("2", "3"))کاربرد تایپهینت ها برای جلوگیری از اجرای کد در صورت خطای تایپی نیست. بلکه کاربردش برای موارد زیر است.
- بهبود خوانایی
- بهبود تجربه کار با ابزارهای توسعه
- کشف سریع تر خطا
بهبود خوانایی
فرض کنید کدی را باز میکنید و تابعی به نام calculate میبینید:
def calculate(items: list[dict[str, float]], threshold: int) -> list[str]:
...با یک نگاه میدانید تابع چه میگیرد و چه برمیگرداند. بنابراین نه لازم است بدنهی تابع را بخوانید، نه یک docstring مفصل بنویسید؛ خودِ کد به وضوح خودش را داکیومنت میکند.
بهبود تجربه کار با ابزارهای توسعه
VS Code و PyCharm وقتی نوعِ متغیر را بدانند، اتوکامپلیتِ دقیق، خطایابیِ لحظهای و پرش به تعریفِ تابع با یک کلیک را در اختیار شما میگذارند. با تایپهینت این ابزارها بر پایهی اطلاعاتِ قطعی کار میکنند، در حالی که بدون آن ناچار به حدس زدناند.
کشف سریع تر خطا
ابزاری به نام mypy کد شما را بدون اجرا بررسی میکند:
mypy my_code.py
اگر به تابعی که int میخواهد رشته بدهید، گزارش میدهد:
error: Argument 1 to "add" has incompatible type "str"; expected "int"
به این ترتیب مشکل را پیش از رسیدن کد به دستِ کاربر پیدا کردهاید.
اولین تایپهینت
یک تابع ساده را در نظر بگیرید:
def greet(name):
return f"Hello, {name}"نسخهی تایپدارش چنین است:
def greet(name: str) -> str:
return f"Hello, {name}"name: str یعنی این پارامتر باید رشته باشد و -> str یعنی خروجی رشته است. کلِ ساختار تایپهینت در پایتون از همین دو قاعده میآید: نوعِ پارامترها و نوعِ خروجی.
محدودیت ذاتی
تایپهینت یک قراردادِ نوشتاری است، نه یک مکانیزمِ اجرایی؛ بنابراین اگر کسی خلافش عمل کند، پایتون خطایی اعلام نمیکند. برای اعمالِ نوعها در زمان اجرا به کتابخانههایی مثل pydantic نیاز دارید. با این حال همین قراردادِ ساده، در صورتی که جدی گرفته شود، دستهی بزرگی از باگها را پیش از تولید حذف میکند.
باور غلط رایج
برخی فکر میکنند تایپهینت پایتون را به زبانی ایستا مثل جاوا تبدیل میکند، در حالی که چنین نیست. پایتون همچنان داینامیک است و تایپهینت فقط یک اعلان است: «بر اساسِ طراحی، این متغیر از این نوع است.» اگر خلافش عمل کنید، نه مفسر اعتراض میکند و نه چیزی مانع شما میشود، زیرا این بررسی بر عهدهی شما و ابزارهایی مثل mypy است.
خلاصه
تایپهینت یک قرارداد نوشتاری است. پایتون در زمان اجرا آن را نادیده میگیرد، اما ابزارها آن را میخوانند و همین سه سود را میسازد:
- خوانایی: امضای تابع نوع ورودی و خروجی را بدون خواندن بدنه نشان میدهد.
- ابزار توسعه: اتوکامپلیت دقیق، خطایابی لحظهای و پرش به تعریف تابع.
- کشف خطا: mypy پیش از اجرا ناسازگاری نوعها را گزارش میکند.
نمادهای پایهای که در این فصل دیدید:
| نماد | معنا |
|---|---|
name: str | این پارامتر باید رشته باشد |
-> str | خروجی تابع رشته است |
-> None | تابع مقداری برنمیگرداند |
mypy file.py | بررسی فایل بدون اجرای آن |
در فصل بعد
در فصل بعد خواهیم دید که چطور میتوان ویرایشگر کد را طوری تنظیم کنیم که بتوان خطا های مربوط به تایپ را در آن مشاهده کنیم.