پایتونمقدماتی۶ دقیقه مطالعهPython 3.12+

مدیریت خطا و استثناها

در این درس تفاوت خطای نحو و استثنا را می‌فهمید، با try و except خطاها را مدیریت می‌کنید، پیام خطا را می‌خوانید و با raise خطای معنادار ایجاد می‌کنید.

آنچه در این درس می‌آموزید

  • خطای نحو را از استثنای زمان اجرا جدا کنید
  • با try و except استثنای مشخصی را بگیرید و برنامه را زنده نگه دارید
  • با raise در تابع خودتان خطای معنادار ایجاد کنید
نویسنده: تیم داکنامه · بازبینی: ۲۰۲۶-۱۰-۳۰
فهرست مطالب

خطا دشمن نیست؛ پیام است. تا اینجا هر خطایی برنامه را همان‌جا متوقف می‌کرد و شما آن را خوانده و کد را اصلاح می‌کردید. اما برنامه‌ای که تحویل کاربر می‌دهید نمی‌تواند هر ورودی خرابی را با توقف جواب دهد: فایلی ممکن است وجود نداشته باشد، کاربر به‌جای عدد حرف تایپ کند، شبکه قطع شود. مدیریت خطا یعنی پیش‌بینی این لحظه‌ها و برنامه‌ریزی برای برخورد باوقار با آن‌ها.

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

خطای نحو و استثنا

نوع اول خطای نحو (Syntax Error) است؛ یعنی جملهٔ کد از قواعد زبان پیروی نمی‌کند:

python
print("hello"
خروجی کد

SyntaxError: '(' was never closed

پایتون خطای نحو را قبل از اجرا می‌گیرد؛ برنامه اصلاً شروع نمی‌شود. نوع دوم استثنا (Exception) است: کد از نظر نحو درست است، ولی در میانهٔ اجرا به مانعی می‌خورد؛ تقسیم بر صفر، اندیس ناموجود، فایل باز نشده. چند استثنای پرتکرار:

استثنا چه وقت رخ می‌دهد
NameError استفاده از نامی که تعریف نشده
TypeError عملیات روی نوع نامناسب، مثل جمع رشته با عدد
ValueError نوع درست اما مقدار نامناسب، مثل int("سلام")
IndexError اندیس بیرون از محدودهٔ لیست
KeyError کلید ناموجود در دیکشنری
ZeroDivisionError تقسیم بر صفر
FileNotFoundError باز کردن فایلی که وجود ندارد

استثنا به‌طور پیش‌فرض برنامه را قطع می‌کند؛ مگر اینکه خودتان بگویید با آن چه کند. ابزار این کار try و except است.

گرفتن استثنا با try و except

بلوک try کدِ مستعد خطا را می‌آزماید؛ اگر استثنای مشخصی رخ دهد، کنترل به بلوک except می‌پرد و برنامه به راهش ادامه می‌دهد:

safe_div.py
def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None

print(safe_divide(10, 2))
print(safe_divide(10, 0))
خروجی کد

5.0 None

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

خواندن پیام خطا

با عبارت as می‌توانید خودِ شیء خطا را بگیرید و پیامش را بخوانید یا ثبت کنید:

convert.py
def to_int(text):
    try:
        return int(text)
    except ValueError as error:
        print(f"Invalid input: {error}")
        return None

print(to_int("42"))
print(to_int("hello"))
خروجی کد

42 Invalid input: invalid literal for int() with base 10: 'hello' None

پیام خطا توضیح دقیق می‌دهد: تابع int نمی‌تواند رشتهٔ «سلام» را به عدد تبدیل کند. خواندن پیام خطا مهارت است؛ نصف رفع اشکال همین است که آن را واقعاً بخوانید.

else و finally

ساختار کامل try دو بخش اختیاری دیگر هم دارد:

cleanup.py
try:
    value = int("7")
except ValueError:
    print("Conversion failed")
else:
    print(f"Value: {value}")
finally:
    print("Done")
خروجی کد

Value: 7 Done

بلوک else فقط وقتی اجرا می‌شود که هیچ خطایی رخ نداده باشد؛ جای منطق موفقیت. بلوک finally در هر دو حالت اجرا می‌شود؛ جای پاک‌کاری مثل بستن فایل و آزاد کردن منابع.

ایجاد خطا با raise

توابع شما هم می‌توانند قراردادهایشان را با خطای معنادار اعمال کنند؛ به این کار «مطرح کردن» استثنا می‌گویند:

validate.py
def set_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative")
    return age

try:
    set_age(-5)
except ValueError as error:
    print(error)
خروجی کد

Age cannot be negative

به‌جای برگرداندن عدد منفی و منتقل‌کردن مسئله به پایین‌دست، تابع همان‌جا اعتراض می‌کند و پیامش روشن است. خطای واضح و زودهنگام، بهتر از دادهٔ خرابِ پنهان است.

تمرین‌ها

تمرین

تمرین ۱ — خواندن امن از لیست

تابعی به نام get_item بنویسید که یک لیست و یک اندیس بگیرد و عضو آن جایگاه را برگرداند؛ اگر اندیس بیرون از محدوده بود، به‌جای خطا رشتهٔ «اندیس خارج از محدوده» را برگرداند و برنامه به کار ادامه دهد.

python
# Write your code here
نمایش راه‌حل
python
def get_item(items, index):
    try:
        return items[index]
    except IndexError:
        return "Index out of range"

print(get_item([10, 20, 30], 1))
print(get_item([10, 20, 30], 7))
خروجی کد

20 Index out of range

::: :::

خلاصه

  • خطای نحو قبل از اجرا گرفته می‌شود؛ استثنا در میانهٔ اجرا رخ می‌دهد و پیش‌فرض برنامه را قطع می‌کند.
  • هر استثنا نام مشخص دارد: ValueError، IndexError، KeyError و چند مورد دیگر.
  • try و except استثنای مشخصی را می‌گیرند و برنامه را زنده نگه می‌دارند؛ با as پیام خطا در دسترس است.
  • else برای موفقیت است و finally همیشه اجرا می‌شود؛ جای پاک‌کاری.
  • با raise در تابع خودتان خطای معنادار ایجاد کنید و هرگز except لخت ننویسید.

حالا که خطاها را مهار می‌کنید، آمادهٔ خطرناک‌ترین کار روزمره‌اید: کار با فایل‌ها؛ جایی که فایل ممکن است نباشد و باید بدانید چه کنید. در درس بعدی می‌نویسیم، می‌خوانیم و پاک می‌کنیم.

منابع و ارجاع‌ها

درس‌های مرتبط