فایل‌ها و مدیریت خطا

مدیریت خطا در پایتون با try و except

۳ دقیقه مطالعه · به‌روزرسانی ۲۲ مهر ۱۴۰۵

یادتان هست که int(input(...)) اگر کاربر به‌جای عدد salam تایپ کند، برنامه را با ValueError متوقف می‌کند؟ در دنیای واقعی کاربرها همیشه چیزهای غیرمنتظره وارد می‌کنند. برنامهٔ خوب نباید با هر ورودی اشتباهی بترکد.

به خطاهایی که هنگام اجرا رخ می‌دهند استثنا (exception) می‌گویند. پایتون اجازه می‌دهد آن‌ها را بگیریم و خودمان تصمیم بگیریم چه کنیم.

ساختار try/except

a = "salam"
try:
    b = int(a)
    print(b)
except ValueError:
    print("it's not a numeric string")
  • کدی که ممکن است خطا بدهد داخل try می‌رود.
  • اگر خطا رخ داد، اجرای try همان‌جا متوقف می‌شود و پایتون سراغ بلوک except می‌رود.
  • اگر خطایی رخ نداد، except کلاً نادیده گرفته می‌شود.

a را به "42" تغییر دهید و دوباره اجرا کنید.

مقایسه کنید با روش حلقه روی کاراکترها که قبلاً برای تشخیص عدد نوشتیم: try/except کوتاه‌تر است و اعداد منفی را هم درست تشخیص می‌دهد (int("-5") کار می‌کند).

💡 به این سبک در پایتون می‌گویند EAFP («بخشش خواستن راحت‌تر از اجازه گرفتن است»): به‌جای اینکه اول همه‌چیز را چک کنید، کار را انجام دهید و اگر خطا داد، مدیریتش کنید.

نوع خطا را مشخص کنید

می‌شود نوشت except: (بدون نوع)، ولی این کار خطرناک است. همهٔ خطاها را می‌گیرد، حتی باگ‌های واقعی خودتان (مثل غلط املایی در اسم متغیر)، و آن‌ها را بی‌صدا پنهان می‌کند:

try:
    b = int(aa)      # غلط املایی! aa تعریف نشده
except:
    print("it's not a numeric string")   # پیام گمراه‌کننده

پیام می‌گوید «عدد نیست»، در حالی که مشکل واقعی NameError است. همیشه دقیقاً خطایی را بگیرید که انتظارش را دارید:

try:
    x = int(input("عدد: "))
    print(100 / x)
except ValueError:
    print("این عدد نیست!")
except ZeroDivisionError:
    print("تقسیم بر صفر ممکن نیست!")

با ورودی‌های 5، abc و 0 امتحان کنید.

الگوی «تا درست نگفتی، دوباره بپرس»

ترکیب while با try/except یکی از پرکاربردترین الگوهاست:

while True:
    try:
        age = int(input("سن خود را وارد کنید: "))
        break                    # ورودی درست بود؛ از حلقه خارج شو
    except ValueError:
        print("لطفاً یک عدد صحیح وارد کنید.")

print(f"سن شما: {age}")

(در ویرایشگر این سایت، چند ورودی را با | جدا کنید، مثلاً abc|bist|25، تا رفتار حلقه را ببینید.)

while True یک حلقهٔ بی‌نهایت عمدی است که فقط با break تمام می‌شود، یعنی وقتی int() بدون خطا اجرا شد.

else و finally

try:
    with open("data.txt", encoding="utf-8") as f:
        content = f.read()
except FileNotFoundError:
    print("فایل پیدا نشد!")
else:
    print("خوانده شد:", len(content), "کاراکتر")   # فقط اگر خطایی رخ نداد
finally:
    print("این خط در هر صورت اجرا می‌شود")
بلوک کِی اجرا می‌شود؟
try همیشه (تا جایی که خطا رخ دهد)
except فقط اگر همان نوع خطا رخ داد
else فقط اگر هیچ خطایی رخ نداد
finally در هر صورت، چه خطا رخ بدهد چه نه؛ برای کارهای پاک‌سازی

💡 دستور with در واقع یک try/finally آماده است که بستن فایل را در finally انجام می‌دهد.

پیام خطا را هم بگیرید

با as می‌شود خود شیء خطا را گرفت و پیامش را نمایش داد:

try:
    int("salam")
except ValueError as e:
    print("خطا:", e)

نکتهٔ مهم: try/except جای کد درست را نمی‌گیرد

try/except برای خطاهایی است که خارج از کنترل شما هستند: ورودی کاربر، فایلی که شاید وجود نداشته باشد، قطع اینترنت. باگ‌های خودتان را با try/except پنهان نکنید؛ درستشان کنید.

تمرین

تابعی به اسم safe_divide(a, b) بنویسید که a / b را برگرداند، ولی اگر b صفر بود به‌جای کرش کردن None برگرداند.

جواب
def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None

print(safe_divide(10, 2))
print(safe_divide(10, 0))