خطا بخشی از برنامه است، نه پایانش
کاربر بهجای عدد «دوازده» تایپ میکند، فایل وجود ندارد، اینترنت قطع است. در پایتون این رویدادها استثنا (exception) ایجاد میکنند؛ اگر کسی آن را نگیرد، برنامه با یک traceback متوقف میشود. traceback را از پایین بخوانید: خط آخر نوع و پیام خطاست و خطوط بالاتر مسیر فراخوانی تا محل خطا.
ساختار کامل
import json
from pathlib import Path
def load_orders(path: Path) -> list:
try:
text = path.read_text(encoding="utf-8")
orders = json.loads(text)
except FileNotFoundError:
print("فایل سفارشها هنوز ساخته نشده؛ از لیست خالی شروع میکنیم.")
return []
except json.JSONDecodeError as e:
print(f"فایل خراب است (خط {e.lineno}): {e.msg}")
raise
else:
print(f"{len(orders)} سفارش بارگذاری شد.")
return orders
finally:
print("پایان تلاش برای خواندن.")
| بخش | کی اجرا میشود |
|---|---|
try | کدی که ممکن است خطا بدهد؛ تا حد ممکن کوتاه |
except X | فقط اگر خطای نوع X (یا زیرکلاسهایش) رخ دهد |
else | فقط اگر try بدون خطا تمام شود |
finally | همیشه؛ با خطا، بدون خطا، حتی بعد از return |
چرا else؟ چون کدی که فقط در صورت موفقیت اجرا میشود نباید داخل try باشد؛ وگرنه ممکن است خطای آن کد را هم بهاشتباه بگیرید و پنهان کنید.
سلسلهمراتب استثناها
BaseException
├── KeyboardInterrupt # Ctrl+C
├── SystemExit # sys.exit()
└── Exception # همهی خطاهای «عادی»
├── ValueError # نوع درست، مقدار غلط: int("دوازده")
├── TypeError # نوع غلط: "۵" + 5
├── LookupError
│ ├── KeyError # کلید دیکشنری نیست
│ └── IndexError # اندیس لیست بیرون از بازه
├── OSError
│ ├── FileNotFoundError
│ └── PermissionError # فایل در اکسل باز و قفل است
├── ZeroDivisionError
└── ...
گرفتن یک کلاس، همهی زیرکلاسهایش را هم میگیرد: except LookupError هم KeyError و هم IndexError را. ترتیب except ها مهم است؛ خاصتر را بالاتر بگذارید.
بدترین الگو
try:
process()
except: # هرگز!
pass
except: خالی حتی Ctrl+C را میگیرد و همراه pass هر باگی را بیصدا دفن میکند. فقط خطاهایی را بگیرید که میدانید چطور مدیریتشان کنید؛ بقیه باید بالا بروند و دیده شوند.
نکتههایی که کمتر کسی میداند
- چند نوع را با تاپل در یک except بگیرید:
except (ValueError, TypeError) as e:. raiseخالی داخل except همان خطا را با traceback اصلی دوباره پرتاب میکند؛ برای «ثبت کن و بگذار برود» عالی است.- اگر در finally از
returnاستفاده کنید، هر خطای در حال انتشار بیصدا بلعیده میشود؛ پایتون ۳.۱۴ برای همین هشدار SyntaxWarning میدهد. - متغیر
eدرexcept ... as eبعد از بلوک except پاک میشود؛ اگر لازمش دارید، در متغیر دیگری ذخیره کنید. - سبک پایتونیک «EAFP» است: بهجای بررسی
if path.exists()و بعد باز کردن (که بینشان ممکن است فایل حذف شود)، مستقیم باز کنید و FileNotFoundError را بگیرید.