برخی خطاهای برنامهای که نوشتهایم در هنگام اجرا و با توجه به شرایط رخ میدهد. اگر ورودی نامناسبی از طرف کاربر وارد شود و یا برنامه نتواند یک فایل را باز کند، با یک نوع خطا که به آن استثنا میگوییم مواجه خواهیم شد. در این مقاله با مدیریت خطا در پایتون آشنا شده و در انتهای آن خواهیم توانست انواع استثنا در پایتون را مدیریت کنیم.
همانطور که میدانید در برنامه نویسی خطاهای مختلفی داریم. برخی خطاهای نوشتاری بوده و برخی خطاهای منطقی هستند. یک نوع دیگر از انواع خطا در برنامه نویسی، خطاهایی است که در هنگام اجرای برنامه رخ داده و موجب توقف آن میشوند. به اینگونه خطاها استثنا یا Exception هم گفته میشود.
فرض کنید در برنامه خود از کاربر خواستهاید تا نمره درس دانشجو را وارد کند. کاربر به جای وارد کردن عدد، از حروف فارسی یا انگلیسی استفاده میکند. اگر شما بخواهید روی این مقدار ورودی پردازشهایی انجام دهید (جمع، تفریق و تقسیم و …) قطعاً با خطا مواجه میشوید.
بهتر است در برنامه نویسی همواره سعی کنیم از خطاهایی که ممکن است رخ دهد جلوگیری کنیم. این کار به وسیله مدیریت خطا (مدیریت استثنا یا Exception Handling) انجام خواهد شد. در ادامه پس از بررسی مدیریت خطا در پایتون، به چگونگی مدیریت استثنا در پایتون خواهیم پرداخت.
خطا در پایتون
در حالت کلی، پایتون برای مدیریت خطاهای پیشبینی نشده در برنامهها دو روش در نظر گرفته است. این دو راهحل عبارتاند از:
- مدیریت استثنا (Exception Handling): در این روش بخشهایی از برنامه که ممکن است باعث ایجاد خطا شود را مدیریت کرده و در صورت بروز خطا، کارهای جایگزین انجام خواهیم داد.
- تست توابع (Assertion): با استفاده از این روش، میتوان ورودی و خروجی یک تابع یا عملیات را بررسی کنیم. در صورتی که مشکلی در ورودی و خروجی مورد انتظار وجود داشت، تصمیماتی برای ادامه برنامه میگیریم.
ما در این مقاله به مورد اول میپردازیم. مورد دوم بیشتر برای مواقعی استفاده میشود که خروجی یک کار قابل پیشبینی است. یعنی ما میدانیم خروجی تابع توان دوم، همواره برابر است با ورودی به توان دو! اما در قسمتهایی که ممکن است خطا به خاطر اشتباه کاربر یا مشکلاتی سیستمی رخ دهد، با استفاده از مدیریت استثنا در پایتون میتوان عملکرد بهتری داشت.
استثنا در پایتون
هر خطایی که در حین اجرای کد پایتون رخ میهد شامل یک نام بوده و از نوعِ خاصی است. استثناها هم نوعی خطا هستند. در نتیجه همه آنها دارای اسم بوده که میتواند به ما در مدیریت بهتر خطاها کمک کند.
فرض کنید یک رشته متنی در اختیار داریم که میخواهیم آن را به عدد تبدیل کنیم. برای این کار از تابع int()
استفاده کرده و رشته را به عنوان ورودی به آن میدهیم. در صورتی که درون رشته فقط عدد وجود داشته باشد، عملیات با موفقیت انجام شده و خروجی ما یک عدد صحیح خواهد بود.
num = "2568"
print( int(num) )
# output: 2568
اما اگر درون رشته ما علاوه بر عدد، حروف انگلیسی نیز وجود داشته باشد، با خطا مواجه خواهیم شد.
num = "25sabz68"
print( int(num) )
در ابتدای خط آخر خطایی که رخ داد، نوع خطا (استثنا) مشخص شده است. خطای ValueError مواقعی رخ میدهد که ورودی تابع اشتباه باشد. در اینجا ما ورودی نادرستی به تابع int()
دادیم که باعث بروز این خطا شد.
Traceback (most recent call last):
File ".\run.py", line 2, in <module>
print( int(num) )
ValueError: invalid literal for int() with base 10: '25sabz68'
در زبان پایتون خطاها و استثناهای مختلفی وجود دارد که میتوانید تمام آنها را در جدول موجود در این صفحه بررسی کنید. برخی از پرتکرارترین استثناها عبارتاند از:
اسم خطا | شرح خطا |
---|---|
Exception | خانواده انواع استثناها |
ArithmeticError | کلاس خطا برای محاسبات عددی |
ZeroDivisionError | خطای خاص از تقسیم عدد بر صفر |
TypeError | ورودی تابع از نوع شئ قابلقبول نیست |
ValueError | مقدار آرگومان ورودی تابع اشتباه است |
مدیریت خطا در پایتون
برای درک بهتر شیوه مدیریت استثنا در پایتون، یک صورت مسئله را در نظر بگیرید.
فرض کنید که میخواهیم از کاربر یک ورودی گرفته، آن را به عدد تبدیل کنیم. سپس آن را به توان 2 رسانده و در خروجی چاپ کنیم.
برای این کار، ابتدا با دستور input()
یک ورودی از کاربر میگیریم. همزمان با دریافت ورودی، آن را به عدد تبدیل کرده و درون متغیر num نگه میداریم. در نهایت با استفاده از دستور print()
و عملگر توان (**
) توان دوم عدد را در خروجی چاپ خواهیم کرد.
num = int( input("Please enter the number: ") )
print( num**2 )
اگر قطعه کد بالا را اجرا کرده و عدد 5 را به عنوان ورودی به آن بدهیم، نتیجه دلخواهمان را خواهیم گرفت.
Please enter the number: 5
25
اگر با تابع input()
و ترفندهای آن آشنا نیستید، میتوانید آموزش گرفتن ورودی از کاربر در پایتون را ببینید.
اما اگر سهواً یا عمداً به جای عدد یک حرف انگلیسی وارد کرده یا ترکیبی از عدد و حروف را به عنوان ورودی به آن بدهیم، با خطای ValueError مواجه خواهیم شد.
Please enter the number: 7s
Traceback (most recent call last):
File ".\run.py", line 1, in <module>
num = int( input("Please enter the number: ") )
ValueError: invalid literal for int() with base 10: '7s'
در ادامه میخواهیم از این خطا در پایتون جلوگیری کنیم. در اصل میخواهیم کاری کنیم که با وجود مشکل در ورودی، برنامه متوقف نشود. دو سناریو برای ادامه برنامه داریم:
- سناریو اول: یک پیغام خطا برای کاربر چاپ کرده و برنامه را به پایان برسانیم.
- سناریو دوم: با چاپ پیغام خطا، مجدداً از کاربر ورودی دریافت کرده و فرآیند را تکرار کنیم.
دستور try برای مدیریت استثنا در پایتون
به کمک دستور try:
میتوانیم یک بلوک مدیریتی داشته باشیم. اگر خطایی در این بلوک رخ دهد، برنامه متوقف نشده و به بلوک دیگر (بلوک except) خواهیم رفت.
دستور except برای مدیریت خطای رخ داده
با استفاده از دستور except:
یک بلوک ایجاد میکنیم. محتوای این بلوک فقط هنگامی اجرا میشوند که در بلوک قبلی (یعنی بلوک try) خطایی رخ داده باشد.
این دستور به دو شیوه تعریف میشود:
- حالت کلی
- حالت مخصوص یک استثنا
حالت کلی دستور except
در این حالت دستور را به صورت except:
نوشته و استفاده میکنیم. هر گونه خطایی که در بلوک try رخ دهد، برنامه وارد این بلوک خواهد شد. فرقی ندارد که خطای رخ داده از نوع ValueError بوده یا خطای مربوط به باز کردن فایل!
حالت خاص مدیریت استثنا با except
در این حالت، در جلوی کلمه کلیدی except و قبل از علامت دو نقطه (:
) نوع خطا را مشخص میکنیم. مثلاً بلوک except زیر در صورتی اجرا میشود که خطای رخ داده در try از نوع ValueError باشد.
except ValueError:
print("Value Error Occurred!")
بلوک finally در مدیریت خطای پایتون
یک بلوک دیگر هم در مدیریت استثنا در پایتون داریم. این بلوک که با کلمه کلیدی finally مشخص میشود، اختیاری بوده و میتوان از آن در یک بلوک try except
استفاده کرد.
این بلوک، پس از اجرای try یا except اجرا خواهد شد. یعنی چه خطا رخ داده باشد و چه نه، محتویات این بلوک اجرا خواهند شد.
یک بلوک مدیریت خطا در پایتون باید حتماً حاوی بخش try
و except
باشد؛ اما بلوک finally
میتواند به دلخواه وجود داشته یا نداشته باشد.
برنامه مدیریت شده مثالی که داشتیم، به صورت زیر خواهد شد.
try:
num = int( input("Please enter the number: ") )
print( num**2 )
except ValueError:
print("لطفا در ورودي فقط عدد وارد کنيد!")
اگر بخواهیم از بلوک finally هم استفاده کرده و پایان برنامه را اعلام کنیم، میتوان کد را به شکل زیر تغییر داد.
try:
num = int( input("Please enter the number: ") )
print( num**2 )
except ValueError:
print("لطفا در ورودي فقط عدد وارد کنيد!")
finally:
print("Ended!")
اگر این قطعه کد را اجرا کرده و ورودی درست به آن بدهیم، خروجی چیزی شبیه زیر خواهد شد:
Please enter the number: 15
225
Ended!
اگر به همین برنامه، یک ورودی اشتباه بدهیم، نتیجهای شبیه به زیر خواهیم داشت:
Please enter the number: 20sabzdanesh
لطفا در ورودي فقط عدد وارد کنيد!
Ended!
هر فرآیند مدیریت خطا در پایتون فقط میتواند شامل یک بخش try
و یک بخش finally
باشد؛ اما میتواند except
های متعددی برای انواع خطاها داشته باشد.
برای مثال اگر در کد بالا بخواهیم جلو خطاهای دیگری که از آنها اطلاع نداریم را بگیریم، میتوانیم از یک بلوک except کلی استفاده کنیم.
try:
num = int( input("Please enter the number: ") )
print( num**2 )
except ValueError:
print("لطفا در ورودي فقط عدد وارد کنيد!")
except:
print("يک خطاي غيرمنتظره رخ داده است!")
finally:
print("Ended!")
مثالی دیگر از مدیریت خطا در پایتون
از جمله موارد دیگری که میتواند هنگام برنامه نویسی باعث دردسر شود، کار با فایل است. به طور کلی کارهای سیستمی ممکن است موجب خطاهای پیشبینی نشده در برنامهی ما شوند. به همین دلیل بهتر است در هنگام انجام عملیاتهایی که مربوط به سیستم عامل است از تکنیکهای مدیریت خطا استفاده کنیم.
در مقالهای دیگر در مورد کار با فایل در پایتون صحبت کردهایم. باز کردن یک فایل ممکن است باعث ایجاد خطاهای ناخواستهای شود. مثلاً:
- فایل توسط برنامه دیگری در حال استفاده است و اجازه باز کردن آن را نداریم.
- فایل وجود ندارد. (مسیر اشتباه یا نام و فرمت اشتباه)
برای جلوگیری از خطا در هنگام باز کردن فایل، بهتر است از بلوکهای try except استفاده کنیم. در مثال زیر ابتدا تلاش کردهایم که یک فایل به نام file.txt
را باز کرده و متنی درون آن بنویسیم.
try:
f = open("file.txt")
f.write("Test from SabzDanesh.com")
except:
print("Something went wrong when writing to the file!")
finally:
f.close()
مدیریت خطای پیشرفته در پایتون
بیایید دو مسئلهای که تا به اینجا مطرح کردیم را با هم ترکیب کنیم. یعنی در ابتدا سعی کنیم یک فایل را باز کرده و خط اول آن را بخوانیم. سپس محتویات آن خط را به عدد تبدیل کنیم. میخواهیم در این مثال، چند خطای ممکن را پیشبینی کرده و مدیریت کنیم.
کد زیر یک نتیجه مطلوب برای این کار خواهد بود.
try:
f = open('file.txt')
s = f.readline()
i = int( s.strip() )
except OSError:
print("We have OS error!")
except ValueError:
print("Could not convert data to an integer!")
except:
print("Unexpected error!")
در برنامه بالا به صورت زیر عمل کردهایم:
- خط اول تا چهارم: تلاش برای انجام عملیاتهای مورد نظر (باز کردن فایل، خواندن آن و تبدیل خط اول به عدد)
- خط پنجم و ششم: در صورتی که خطایی در هنگام باز کردن فایل رخ دهد، آن را مدیریت میکنیم.
- خط هفتم و هشتم: برای مدیریت خطای تبدیل رشته به عدد در نظر گرفته شده است.
- خط نهم و دهم: برای مدیریت خطای ناخواسته در پایتون این بلوک را نوشتهایم.
تولید استثنا در پایتون
هنگامی که در حال نوشتن یک تابع یا کلاس در برنامه خود هستیم، ممکن است لازم داشته باشیم تا در صورت وجود شرایطی خاص، یک استثنا ایجاد کنیم. این استثنا میتواند از استثناهای پیشفرض پایتون بوده و یا استثنایی باشد که خودمان آن را ایجاد کردهایم.
در هر حال، برای تولید خطا در پایتون میتوان از کلمه کلیدی raise
استفاده کرد. ساختار کلی این دستور به صورت زیر است.
raise [Exception [, args [, traceback]]]
اولین آرگومان آن ضروری و دو مورد دیگر اختیاری هستند.
- آرگومان اول (Exception) : نام یا نوع استثنا و خطایی است که میخواهیم اتفاق بیافتد.
- آرگومان دوم (args) : این مقدار به عنوان آرگومان ورودی exception صدا زده شده در نظر گرفته میشود.
- پارامتر سوم (traceback) : نشاندهنده یک پشته از خطاها و رویدادهای مربوط به خطای اتفاق افتاده است.
در قطعه کد زیر، اگر مقدار متغیر i
از 23 بیشتر بود، یک خطای ValueError تولید میکنیم.
if i > 23:
raise ValueError
جمعبندی: مدیریت خطا در پایتون
در این آموزش با مدیریت خطا در پایتون آشنا شدیم. مدیریت خطا یا مدیریت استثنا در پایتون بسیار ساده اما کاربردی است. ما میتوانیم با استفاده از یک ساختار بلوکی نسبت به مدیریت خطاهای پیشبینی نشده اقدام کنیم.
در ابتدا با دستور try
سعی میکنیم که کاری را انجام دهیم. در صورتی که مشکلی به وجود بیاید با استفاده از دستور except
آن را مدیریت کرده یا پیغام مناسبی را در خروجی نمایش میدهیم. در انتها به کمک finally
میتوان یک بلوک را در هر شرایطی اجرا کرد. یعنی چه بخش try با موفقیت اجرا شده و چه وارد except شده باشیم، بلوک finally اجرا خواهد شد.
همچنین یاد گرفتیم که میتوان چند بلوک except برای مدیریت خطاهای مختلف در برنامه تعریف کرد.