برخی خطاهای برنامهای که نوشتهایم در هنگام اجرا و با توجه به شرایط رخ میدهد. اگر ورودی نامناسبی از طرف کاربر وارد شود و یا برنامه نتواند یک فایل را باز کند، با یک نوع خطا که به آن استثنا میگوییم مواجه خواهیم شد. در این مقاله با مدیریت خطا در پایتون آشنا شده و در انتهای آن خواهیم توانست انواع استثنا در پایتون را مدیریت کنیم.
فهرست محتوای آموزش
• خطا در پایتون
خطا در پایتون
در حالت کلی، پایتون برای مدیریت خطاهای پیشبینی نشده در برنامهها دو روش در نظر گرفته است. این دو راهحل عبارتاند از:
• مدیریت استثنا (Exception Handling): در این روش بخشهایی از برنامه که ممکن است باعث ایجاد خطا شود را مدیریت کرده و در صورت بروز خطا، کارهای جایگزین انجام خواهیم داد.
• تست توابع (Assertion): با استفاده از این روش، میتوان ورودی و خروجی یک تابع یا عملیات را بررسی کنیم. در صورتی که مشکلی در ورودی و خروجی مورد انتظار وجود داشت، تصمیماتی برای ادامه برنامه میگیریم.
ما در این مقاله به مورد اول میپردازیم. مورد دوم بیشتر برای مواقعی استفاده میشود که خروجی یک کار قابل پیشبینی است. یعنی ما میدانیم خروجی تابع توان دوم، همواره برابر است با ورودی به توان دو! اما در قسمتهایی که ممکن است خطا به خاطر اشتباه کاربر یا مشکلاتی سیستمی رخ دهد، با استفاده از مدیریت استثنا در پایتون میتوان عملکرد بهتری داشت.
استثنا در پایتون
هر خطایی که در حین اجرای کد پایتون رخ میهد شامل یک نام بوده و از نوعِ خاصی است. استثناها هم نوعی خطا هستند. در نتیجه همه آنها دارای اسم بوده که میتواند به ما در مدیریت بهتر خطاها کمک کند.
فرض کنید یک رشته متنی در اختیار داریم که میخواهیم آن را به عدد تبدیل کنیم. برای این کار از تابع ()int استفاده کرده و رشته را به عنوان ورودی به آن میدهیم. در صورتی که درون رشته فقط عدد وجود داشته باشد، عملیات با موفقیت انجام شده و خروجی ما یک عدد صحیح خواهد بود.
"num = "2568
print( int(num) )
output: 2568#
اما اگر درون رشته ما علاوه بر عدد، حروف انگلیسی نیز وجود داشته باشد، با خطا مواجه خواهیم شد.
"num = "25kabir68
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: '25kabir68
در زبان پایتون خطاها و استثناهای مختلفی وجود دارد که میتوانید تمام آنها را در جدول موجود در این صفحه بررسی کنید. برخی از پرتکرارترین استثناها عبارتاند از:
اسم خطا | شرح خطا |
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) خطایی رخ داده باشد.
این دستور به دو شیوه تعریف میشود:
1. حالت کلی
2. حالت مخصوص یک استثنا
حالت کلی دستور 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: 20kabirapp
لطفا در ورودي فقط عدد وارد کنيد!
!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 برای مدیریت خطاهای مختلف در برنامه تعریف کرد.
نام | تعداد آزمون | میزان موفقیت | |
---|---|---|---|
َAmeneh Darvishzadeh | 1 | 100/00 % | |
Mehrad Hashemi | 1 | 100/00 % | |
Noushmehr Norsobhi | 1 | 100/00 % | |
محمدحسین میرزایی | 1 | 100/00 % | |
مهدی حسین پور آقائی | 1 | 100/00 % | |
Farnoush Toghiany | 21 | 98/36 % | |
zahra namdari | 46 | 98/21 % | |
یاسمن محمدی پور | 4 | 98/08 % | |
Tara Mohammadi | 3 | 96/43 % | |
yasaman mohamadipur | 51 | 95/86 % | |
مهدی هنرمند | 1 | 95/24 % | |
محمدجواد ملائی اردستانی | 3 | 94/44 % | |
Arzhang Saberi | 4 | 93/33 % | |
Soheila Karimi | 124 | 92/73 % | |
aram farhmand | 10 | 92/31 % | |
یاشار اسکندری | 98 | 91/14 % | |
عباس پورمیدانی | 1 | 90/00 % | |
پریسا سلوکی شهرضایی | 72 | 89/49 % | |
ارشیا قلمکاری | 33 | 89/23 % | |
Matin Azimipour | 30 | 88/17 % |