چالش
تابعی داریم که سبد خرید را حساب میکند:
def calculate_total(items, discount, apply_tax):
...از امضای این تابع معلوم نیست items لیست است یا دیکشنری یا لیستی از دیکشنریها، discount عدد است یا درصدی به صورت رشته، و apply_tax بولین است یا نرخ مالیات. برای رسیدن به پاسخ باید بدنهی تابع را بخوانید یا جایی را که تابع صدا زده میشود پیدا کنید. این فصل نشان میدهد که چگونه چنین ابهامی از ابتدا شکل نگیرد.
تایپهای پایه
پایتون چهار نوعِ ساده دارد که بیشترِ کدها از آنها ساخته میشوند:
name: str = "Ali"
age: int = 32
price: float = 99.5
is_active: bool = Trueروی توابع هم همین شکل کار میکند:
def create_user(name: str, age: int, is_active: bool) -> str:
return f"{name} ({age})"وقتی تابع چیزی برنمیگرداند
اگر تابعی مقداری برنمیگرداند، خروجیاش None است:
def log_error(message: str) -> None:
print(f"ERROR: {message}")-> None را حتماً بنویسید، زیرا در نبودِ آن mypy در حالت سختگیرانه نوعِ خروجی را نامعلوم فرض میکند و خطا نمایش میدهد.
مقدار پیشفرض
در صورتی که بخواهید برای ورودی های تابع، مقدار پیشفرض در نظر بگیرید، هینت و مقدار پیشفرض کنار هم میآیند:
def create_user(name: str, age: int = 18, is_active: bool = True) -> str:
...ترتیب اهمیت دارد: اول نوع، بعد مساوی، بعد مقدار.
لیست
names: list[str] = ["Ali", "Sara"]
scores: list[int] = [90, 85, 77]list[str] یعنی لیستی که همهی عناصرش رشتهاند.
def get_names(users: list[dict]) -> list[str]:
return [u["name"] for u in users]دیکشنری
dict دو نوع تایپهینت میگیرد: کلید و مقدار.
prices: dict[str, float] = {"laptop": 1200.0, "mouse": 25.5}اولی نوعِ کلید است و دومی نوعِ مقدار.
تاپل
تاپل دو حالت دارد و اشتباه گرفتنِ این دو حالت رایج است.
تاپلِ ثابت — تعداد و نوعِ هر عنصر مشخص است:
point: tuple[int, int] = (10, 20)
user_row: tuple[int, str, bool] = (1, "Ali", True)تاپلِ کشسان — همهی عناصر یک نوع دارند و تعدادشان معلوم نیست:
scores: tuple[int, ...] = (90, 85, 77, 60)توجه کنید که آن سه نقطه بخشی از سینتکس است، نه جای خالی، و معنایش «و همینطور تا آخر» است.
مجموعه
tags: set[str] = {"python", "typing"}تودرتو
این نوعها را به هر اندازه که لازم باشد میتوان تو در تو نوشت:
orders: dict[str, list[int]] = {
"user_1": [101, 102],
"user_2": [103],
}این یعنی دیکشنریای که کلیدش رشته و مقدارش لیستی از عددهای صحیح است.
در برخورد با تایپی مثل نمونهی زیر:
def process(data: dict[str, list[tuple[int, str]]]) -> None:
...به یاد داشته باشید که راهِ خواناتری وجود دارد؛ در فصل ششم با TypeAlias آشنا میشوید.
سبک قدیمی
در کدهای قبل از پایتون ۳.۹ این شکل دیده میشود:
from typing import List, Dict, Tuple, Set
names: List[str] = ["Ali"]
prices: Dict[str, float] = {}این شکل هنوز کار میکند، اما دیگر توصیه نمیشود، زیرا از پایتون ۳.۹ به بعد خودِ list و dict و tuple نوع میپذیرند و به import نیازی نیست. قاعدهی ساده این است: کدِ جدید را با حرفِ کوچک بنویسید و کدِ قدیمی را هر وقت به آن دست زدید بهمرور اصلاح کنید.
باور غلط رایج
بسیاری فکر میکنند list[str] جلوی اضافه کردنِ عدد به لیست را میگیرد، در حالی که چنین نیست:
names: list[str] = ["Ali"]
names.append(42) # پایتون هیچ اعتراضی نمیکندتنها mypy و Pylance به این کد اعتراض میکنند.
خلاصه
در این فصل گرامر پایهی تایپهینت را آموختید: نوع پارامترها، نوع خروجی و نوع محتوای کالکشنها. با همین چند الگو میتوانید امضای بیشتر توابع پروژه را گویا کنید.
- امضای تابع: نوع ورودی را با
x: intو نوع خروجی را با-> intاعلام کنید. - تابع بدون خروجی:
-> Noneرا حتماً بنویسید، وگرنه mypy در حالت سختگیرانه بررسی را ادامه نمیدهد. - کالکشنها: نوع محتوا داخل براکت میآید و میتواند تودرتو باشد، مانند
dict[str, list[int]]. - دو چهرهی تاپل: حالت ثابت
tuple[int, str]تعداد معینی دارد و حالت کشسانtuple[int, ...]تعداد نامعلوم. - سبک تازه: از پایتون ۳.۹ به بعد
ListوDictرا کنار بگذارید و از نام کوچک استفاده کنید.
| سینتکس | معنا | نمونه |
|---|---|---|
str ، int ، float ، bool | نوعهای پایه | age: int = 32 |
-> None | تابع مقداری برنمیگرداند | def log(m: str) -> None |
list[str] | لیستی از رشتهها | names: list[str] |
dict[str, float] | دیکشنری با کلید و مقدار معین | prices: dict[str, float] |
tuple[int, ...] | تاپل کشسان از یک نوع | scores: tuple[int, ...] |
set[str] | مجموعهی رشتهها | tags: set[str] |
در فصل بعد
تا اینجا هر متغیر دقیقاً یک نوع داشت، در حالی که در بسیاری از کدها یک مقدار میتواند غایب باشد یا از چند نوع مختلف باشد. فصل بعد به همین دو حالت میپردازد.