کار با استثناء در پایتون: مقدمه ای دقیق و بصری
کار با استثناء در پایتون: مقدمه ای دقیق و بصری
خوش آمدید! در این مقاله ، نحوه برخورد با استثنائات در پایتون یاد خواهید گرفت.در ادامه با وبلاگ هاستینجا همراه باشید.
در این مقاله با موارد زیر آشنا خواهیم شد:
- استثناها
- هدف از رسیدگی به استثنا
- بند امتحان
- بند جز
- بند دیگر
- بند آخر
- چگونه استثنائات را مطرح کنیم
اماده اید؟ شروع میکنیم!
معرفی به استثنائات
ما با استثنا شروع خواهیم کرد:
- آنها چه چیزی هستند؟
- چرا آنها مرتبط هستند؟
- چرا باید به آنها رسیدگی کنید؟
طبق مستندات پایتون:
خطاهایی که هنگام اجرا اعدام شده استثنا خوانده می شوند و بدون قید و شرط کشنده نیستند.
استثنائات هنگامی ایجاد می شود که برنامه هنگام اجرای آن با خطایی مواجه شود.
آنها جریان طبیعی برنامه را مختل می کنند و معمولاً به طور ناگهانی آن را خاتمه می دهند. برای جلوگیری از این ، می توانید آنها را بگیرید و به طور مناسب با آنها رفتار کنید.
احتمالاً آنها را در طول پروژه های برنامه نویسی خود دیده اید.
اگر تاکنون سعی کرده اید در پایتون تقسیم صفر کنید ، حتما این پیام خطا را دیده اید:
1 2 3 4 5 |
>>> a = 5/0 Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> a = 5/0 ZeroDivisionError: division by zero |
اگر سعی کردید رشته ای را با یک فهرست نامعتبر فهرست کنید ، قطعاً این پیام خطا را دریافت می کنید:
1 2 3 4 5 6 |
>>> a = "Hello, World" >>> a[456] Traceback (most recent call last): File "<pyshell#3>", line 1, in <module> a[456] IndexError: string index out of range |
اینها نمونه هایی از استثنائات هستند.
استثنائات رایج
استثنائات مختلف زیادی وجود دارد و همه آنها در موقعیت های خاص مطرح می شوند.برخی از استثنائاتی که به احتمال زیاد هنگام کار روی پروژه های خود مشاهده خواهید کرد عبارتند از:
IndexError – وقتی سعی می کنید لیستی را فهرست کنید ، مرتب سازی کنید یا فراتر از مرزهای مجاز ایندکس کنید. مثلا:
1 2 3 4 5 6 |
>>> num = [1, 2, 6, 5] >>> num[56546546] Traceback (most recent call last): File "<pyshell#7>", line 1, in <module> num[56546546] IndexError: list index out of range |
KeyError – هنگام تلاش برای دسترسی به مقدار کلیدی که در فرهنگ لغت وجود ندارد ، مطرح می شوید. مثلا:
1 2 3 4 5 6 |
>>> students = {"Nora": 15, "Gino": 30} >>> students["Lisa"] Traceback (most recent call last): File "<pyshell#9>", line 1, in <module> students["Lisa"] KeyError: 'Lisa' |
NameError – وقتی نامی که در کد به آن مراجعه می کنید وجود ندارد. مثلا:
1 2 3 4 5 |
>>> a = b Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> a = b NameError: name 'b' is not defined |
TypeError – هنگامی که یک عمل یا عملکرد بر روی یک شیء از نوع نامناسب اعمال می شود ، مطرح می شود. مثلا:
1 2 3 4 5 |
>>> (5, 6, 7) * (1, 2, 3) Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> (5, 6, 7) * (1, 2, 3) TypeError: can't multiply sequence by non-int of type 'tuple' |
ZeroDivisionError – وقتی سعی می کنید صفر تقسیم شود ، مطرح می شود.
1 2 3 4 5 |
>>> a = 5/0 Traceback (most recent call last): File "<pyshell#13>", line 1, in <module> a = 5/0 ZeroDivisionError: division by zero |
نکات: برای کسب اطلاعات بیشتر در مورد انواع دیگر استثنائات داخلی ، لطفاً به مقاله موجود در مستندات پایتون مراجعه کنید.
آناتومی یک استثناء
من مطمئن هستم که شما باید در این پیام های خطا به الگوی کلی توجه کرده باشید. بیایید قطعه ساختار کلی آنها را به صورت جداگانه تجزیه کنیم:
ابتدا این خط را می یابیم (به تصویر زیر مراجعه کنید). یک ردیابی در واقع لیستی است که جزئیات تماس های عملکردی را که قبل از مطرح شدن استثنا انجام شده است ، شرح می دهد.ردیابی به شما در فرآیند اشکال زدایی کمک می کند زیرا می توانید ترتیب تماسهای عملکردی را که منجر به استثناء شده است ، تجزیه و تحلیل کنید:
1 |
Traceback (most recent call last): |
سپس ، این خط (مسیر زیر را مشاهده می کنید) با مسیر پرونده و خطی که استثناء را ایجاد کرده است ، می بینیم. در این حالت ، مسیر پوسته <pyshell#0> بود زیرا نمونه به طور مستقیم در IDLE اجرا شد.
1 2 |
File "<pyshell#0>", line 1, in <module> a - 5/0 |
نکته: اگر خطی که استثناء را ایجاد کرد متعلق به یک تابع است ، <module> با نام تابع جایگزین می شود.
سرانجام ، ما یک پیام توصیفی می بینیم که نوع استثناء را شرح می دهد و اطلاعات اضافی را برای کمک به اشکال زدایی در کد به ما کمک می کند:
1 |
NameError: name 'a' is not defined |
مدیریت استثنا: هدف و متن
ممکن است بپرسید: چرا می خواهم استثنائات را اداره کنم؟ چرا این برای من مفید است؟ با استفاده از استثنائات ، می توانید جریان جایگزین اعدام را برای جلوگیری از تصادف غیرمنتظره برنامه خود فراهم کنید.
مثال: ورودی کاربر
تصور کنید که اگر کاربری که با برنامه شما کار می کند یک ورودی نامعتبر وارد کند چه اتفاقی می افتد زیرا یک عمل نامعتبر در طی فرایند انجام می شود. اگر برنامه شما این کار را به درستی انجام ندهد ، ناگهان خراب می شود و کاربر تجربه بسیار ناامید کننده ای با محصول شما خواهد داشت.
اما اگر استثنا را برطرف کنید ، می توانید جایگزینی برای بهبود تجربه کاربر ارائه دهید.شاید بتوانید یک پیام توصیفی از کاربر بخواهید که یک ورودی معتبر را وارد کند ، نمایش داده می شود ، یا می توانید یک مقدار پیش فرض برای ورودی ارائه دهید. بسته به بستر ، شما می توانید انتخاب کنید که چه کاری باید انجام شود وقتی این اتفاق می افتد ، و این جادوی استفاده از خطا است. این می تواند روزی را که اتفاقات غیرمنتظره رخ می دهد نجات دهد.
چه چیزی در پشت صحنه اتفاق می افتد؟
اصولاً وقتی استثنائی را اجرا می کنیم ، به برنامه می گوییم اگر استثنائی مطرح شود ، چه باید کرد. در این صورت ، جریان “جایگزین” اعدام به نجات می رسد. اگر هیچ استثنائی مطرح نشود ، کد مطابق پیش بینی اجرا می شود.
زمان برای کد: سعی کنید … به جز بیانیه
اکنون که می دانید استثنائات چیست و چرا باید آنها را کنترل کنید ، شروع به غوطه ور شدن در ابزارهای داخلی می کنیم که زبانهای پایتون برای این منظور ارائه می دهد.
اول ، ما اساسی ترین جمله را داریم: سعی کنید … به جز.
بیایید این را با یک مثال ساده نشان دهیم. ما این برنامه کوچک را داریم که از کاربر می خواهد نام دانش آموز را برای نمایش سن خود وارد کند:
1 2 3 4 5 6 7 |
students = {"Nora": 15, "Gino": 30} def print_student_age(): name = input("Please enter the name of the student: ") print(students[name]) print_student_age() |
توجه کنید که ما در حال حاضر اعتبار ورودی کاربر را تأیید نمی کنیم ، بنابراین ممکن است کاربر مقادیر نامعتبر (نام هایی را که در فرهنگ لغت نیستند) وارد کنید و عواقب آن فاجعه بار خواهد بود زیرا اگر یک KeyError مطرح شود ، این برنامه خراب می شود:
1 2 3 4 5 6 7 8 9 10 |
# User Input Please enter the name of the student: "Daniel" # Error Message Traceback (most recent call last): File "<path>", line 15, in <module> print_student_age() File "<path>", line 13, in print_student_age print(students[name]) KeyError: '"Daniel"' |
ترکیب
ما می توانیم این کار را به خوبی با استفاده از امتحان کنیم … به جز. این ترکیب اصلی است:
به عنوان مثال ، ما سعی می کنیم اضافه کنیم … به جز عبارت درون تابع. بیایید این قطعه را به صورت جداگانه بشکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
students = {"Nora": 15, "Gino": 30} def print_student_age(): while True: try: name = input("Please enter the name of the student: ") print(students[name]) break except: print("This name is not registered") print_student_age() |
اگر “بزرگنمایی” کنیم ، سعی می کنیم … به جز بیانیه:
1 2 3 4 5 6 |
try: name = input("Please enter the name of the student: ") print(students[name]) break except: print("This name is not registered") |
- وقتی تابع فراخوانی می شود ، بند سعی اجرا می شود. در صورت عدم استثناء ، برنامه مطابق آنچه انتظار می رود اجرا می شود.
- اما اگر یک استثناء در بند آزمایشی مطرح شود ، جریان اجرای آن بلافاصله به بندهای دیگری می رود تا بتوانید استثنا را کنترل کنید.
توجه: این کد در یک حلقه مدنظر قرار دارد تا در صورت عدم اعتبار مقدار درخواست کاربر را ادامه دهد. این یک مثال است:
1 2 3 |
Please enter the name of the student: "Lulu" This name is not registered Please enter the name of the student: |
این عالیه ، درسته؟ اکنون می توانیم اگر مقدار نامعتبر باشد ، درخواست ورودی کاربر را ادامه دهیم.
در حال حاضر ، ما با استثناء بند ، همه استثنائات ممکن را با یک مورد در دست می گیریم. اما اگر فقط بخواهیم نوع خاصی از استثنا را کنترل کنیم ، چه می شود؟ بیایید ببینیم چگونه می توانیم این کار را انجام دهیم.
ابتلا به استثنائات خاص
از آنجا که همه نوع استثناء به همان روش اداره نمی شوند ، می توانیم مشخص کنیم کدام استثنائات را دوست داریم با این نحو مقابله کنیم:
این یک نمونه است. در صورت ورود کاربر صفر به عنوان مخرج ، ما از استثنا ZeroDivisionError استفاده می کنیم:
1 2 3 4 5 6 7 8 9 10 11 |
def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except ZeroDivisionError: print("Please enter a valid denominator.") divide_integers() |
این نتیجه خواهد بود:
1 2 3 4 5 6 7 8 9 |
# First iteration Please enter the numerator: 5 Please enter the denominator: 0 Please enter a valid denominator. # Second iteration Please enter the numerator: 5 Please enter the denominator: 2 2.5 |
ما به درستی رسیدگی می کنیم. اما … اگر نوع دیگری از استثناء مطرح شود ، برنامه به طرز مهربانی از عهده آن بر نمی آید.
در اینجا مثالی از ValueError داریم زیرا یکی از مقادیر یک float است نه int:
1 2 3 4 5 6 7 8 |
Please enter the numerator: 5 Please enter the denominator: 0.5 Traceback (most recent call last): File "<path>", line 53, in <module> divide_integers() File "<path>", line 47, in divide_integers b = int(input("Please enter the denominator: ")) ValueError: invalid literal for int() with base 10: '0.5' |
ما می توانیم نحوه کار با انواع مختلف استثنا را تنظیم کنیم.
به جز بندهای متعدد
برای انجام این کار ، ما باید چندین مورد به جز بند اضافه کنیم تا انواع مختلفی از استثنائات متفاوت انجام شود.
مطابق مستندات پایتون:
یک بیانیه آزمایشی ممکن است بیش از یک بند داشته باشد تا بندهایی را برای استثنائات مختلف مشخص کند. حداکثر یک اداره کننده اعدام خواهد شد.
در این مثال ، دو مورد به جز بند داریم. یکی از آنها ZeroDivisionError را کنترل می کند و دیگری ValueError را کنترل می کند ، دو نوع استثنائی که می توانند در این بلوک آزمایشی مطرح شوند.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except ZeroDivisionError: print("Please enter a valid denominator.") except ValueError: print("Both values have to be integers.") divide_integers() |
نکته: شما باید تعیین کنید که کدام نوع استثنائات ممکن است در بلوک آزمایشی مطرح شود تا به درستی آنها را کنترل کنید.
کار با استثناء در پایتون: مقدمه ای دقیق و بصری
استثنائات متعدد ، یک بند جز است
همچنین می توانید انواع مختلف استثناء را با یک بند به جز بند انجام دهید.
مطابق مستندات پایتون:
یک بند به جز یک استثناء ممکن است چندین استثناء را به عنوان یک دستگیره پرانتز نامگذاری کند.
این مثالی است که در آن دو استثناء (ZeroDivisionError و ValueError) با یک مورد به جز بند مشابه داریم:
1 2 3 4 5 6 7 8 9 10 |
def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except (ZeroDivisionError, ValueError): print("Please enter valid integers.") divide_integers() |
خروجی برای دو نوع استثناء یکسان خواهد بود زیرا به جز بند با آنها کار می شود:
1 2 3 |
Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. |
1 2 3 |
Please enter the numerator: 0.5 Please enter valid integers. Please enter the numerator: |
کار با استثناء در پایتون: مقدمه ای دقیق و بصری
رسیدگی به استثنائات ناشی از توابع فراخوانده شده در بند آزمون
جنبه جالب توجه استثناء این است که اگر استثنائی در عملکردی که قبلاً در بند آزمایشی یک عملکرد دیگر نامیده می شد مطرح شود و خود عملکرد آن را انجام ندهد ، در صورت وجود مواردی غیر از بند ، تماس گیرنده آن را اداره می کند.
مطابق مستندات پایتون:
دارندگان استثنائات فقط در موارد غیرقابل استثناء شرط نمی کنند اگر بلافاصله در بند محاکمه اتفاق بیفتند ، بلکه در صورت وجود عملکردهایی که در بند امتحان نامیده می شوند (حتی غیر مستقیم) نیز وجود دارند.
بیایید نمونه ای برای توضیح این مورد مشاهده کنیم:
1 2 3 4 5 6 7 8 9 10 11 |
def f(i): try: g(i) except IndexError: print("Please enter a valid index") def g(i): a = "Hello" return a[i] f(50) |
ما تابع f و تابع g را داریم. در بند آزمایشی g تماس می گیرد. با استدلال ۵۰ ، g یک IndexError ایجاد می کند زیرا شاخص ۵۰ برای رشته a معتبر نیست.
اما g خودش به استثنا عمل نمی کند. توجه کنید که چگونه هیچ تلاشی وجود ندارد … به جز عبارت در عملکرد g. از آنجا که این استثناء را شامل نمی شود ، آن را “f” ارسال می کند تا ببیند آیا می تواند آن را اداره کند ، همانطور که در شکل زیر مشاهده می کنید:
از آنجا که f می داند چگونه IndexError را مدیریت کند ، وضعیت به طرز مهربانی اداره می شود و این خروجی است:
1 |
Please enter a valid index |
توجه: اگر f این استثنا را به کار نمی برد ، برنامه با پیام خطای پیش فرض برای یک IndexError به طور ناگهانی به پایان می رسید.
دسترسی به جزئیات خاص استثنائات
استثنائات اشیاء در پایتون هستند ، بنابراین می توانید استثنائی را که برای یک متغیر مطرح شده اختصاص دهید. با این روش می توانید توضیحات پیش فرض استثنا را چاپ کرده و به آرگومان های آن دسترسی پیدا کنید.
مطابق مستندات پایتون:
1 |
The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. |
در اینجا یک مثال داریم (مثال زیر را ببینید) اینکه نمونه ZeroDivisionError را به متغیر e اختصاص دادیم. سپس ، ما می توانیم از این متغیر در بند جز برای دسترسی به نوع استثنا ، پیام آن و استدلال استفاده کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) # Here we assign the exception to the variable e except ZeroDivisionError as e: print(type(e)) print(e) print(e.args) divide_integers() |
خروجی مربوطه:
1 2 3 4 5 6 7 8 9 10 11 |
Please enter the numerator: 5 Please enter the denominator: 0 # Type <class 'ZeroDivisionError'> # Message division by zero # Args ('division by zero',) |
نکته: اگر با روشهای خاص آشنا هستید ، مطابق مستندات پایتون: “برای راحتی ، نمونه استثنائی __str __ () را تعریف می کند ، بنابراین آرگومان ها می توانند بطور مستقیم و بدون نیاز به مرجع چاپ شوند.”
اکنون بیایید اضافه کنیم: بند “other”
بند دیگر اختیاری است ، اما این یک ابزار عالی است زیرا به ما امکان می دهد کدی را اجرا کنیم که فقط در صورت عدم استثناء در بند آزمایشی ، باید اجرا شود.
مطابق مستندات پایتون:
محاکمه … به جز بیانیه یک بند دیگر اختیاری دارد ، که در صورت وجود ، باید همه را به جز بندها دنبال کند. برای کدی مفید است که اگر بند آزمایشی استثنائی ایجاد نکند ، باید اجرا شود.
در اینجا مثالی از استفاده از بند دیگر آورده شده است:
1 2 3 4 5 6 7 8 9 10 11 12 |
def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) result = a / b except (ZeroDivisionError, ValueError): print("Please enter valid integers. The denominator can't be zero") else: print(result) divide_integers() |
در صورت عدم استثناء ، نتیجه چاپ می شود:
1 2 3 |
Please enter the numerator: 5 Please enter the denominator: 5 1.0 |
اما اگر استثنائی مطرح شود ، نتیجه چاپ نمی شود:
1 2 3 |
Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. The denominator can't be zero |
نکته: طبق مستندات پایتون:
استفاده از بند دیگر بهتر از افزودن کد اضافی به بند آزمایشی است زیرا از ابتکار تصادفی استثنائی که توسط محافظت از کد توسط سعی در افزایش … جلوگیری نمی کند جلوگیری می کند.
کار با استثناء در پایتون: مقدمه ای دقیق و بصری
سرانجام بند آخر
بند آخر آخرین بند این دنباله است. این اختیاری است ، اما اگر آن را وارد کنید ، باید آخرین بند در دنباله باشد. بند آخر همیشه اجرا می شود ، حتی اگر استثنائی در بند محاکمه مطرح شود.
مطابق مستندات پایتون:
اگر یک بند آخر وجود داشته باشد ، قبل از اتمام دستور آزمایشی ، بند آخر به عنوان آخرین کار اجرا می شود. بند آخر این نتیجه را ایجاد می کند یا خیر که عبارت try یک استثنا را ایجاد می کند یا نه.
بند آخر معمولاً برای انجام اقدامات “پاکسازی” استفاده می شود که همیشه باید تکمیل شود. به عنوان مثال ، اگر ما در یک بند تلاش با یک پرونده کار می کنیم ، همیشه باید پرونده را ببندیم ، حتی اگر هنگام کار با داده ها ، یک استثنا ایجاد شده باشد.
در اینجا نمونه ای از بند آخر آورده شده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) result = a / b except (ZeroDivisionError, ValueError): print("Please enter valid integers. The denominator can't be zero") else: print(result) finally: print("Inside the finally clause") divide_integers() |
این خروجی زمانی است که هیچ استثنائی مطرح نشده است:
1 2 3 4 |
Please enter the numerator: 5 Please enter the denominator: 5 1.0 Inside the finally clause |
این خروجی وقتی استثنائی مطرح شد:
1 2 3 4 |
Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. The denominator can't be zero Inside the finally clause |
توجه کنید که بند آخر همیشه چگونه اجرا می شود.
نکته مهم: به یاد داشته باشید که بند دیگر و بند آخر اختیاری است ، اما اگر تصمیم دارید که هر دو را درج کنید ، بند آخر باید آخرین بند در دنباله باشد.
استثنائات
اکنون که می دانید چگونه استثنائات را در پایتون انجام دهید ، می خواهم این نکته مفید را با شما به اشتراک بگذارم: شما همچنین می توانید انتخاب کنید که چه زمانی استثنائات را در کد خود مطرح کنید.
این می تواند برای سناریوهای خاص مفید باشد. بیایید ببینیم چگونه می توانید این کار را انجام دهید:
این خط یک ValueError را با یک پیام سفارشی بالا می برد.
در اینجا ما یک نمونه از عملکردی را داریم که مقدار مقادیر یک لیست یا تاپل یا شخصیت های یک رشته را چاپ می کند. اما شما تصمیم گرفتید که لیست ، توپل یا رشته را به طول ۵ در نظر بگیرید. شما عملکرد را با یک عبارت if شروع می کنید که بررسی می کند اگر طول داده های آرگومان ۵ باشد بررسی می کند. :
1 2 3 4 5 6 7 8 9 |
def print_five_items(data): if len(data) != 5: raise ValueError("The argument must have five elements") for item in data: print(item) print_five_items([5, 2]) |
خروجی:
1 2 3 4 5 6 |
Traceback (most recent call last): File "<path>", line 122, in <module> print_five_items([5, 2]) File "<path>", line 117, in print_five_items raise ValueError("The argument must have five elements") ValueError: The argument must have five elements |
توجه کنید که چگونه خط آخر پیام توصیفی را نشان می دهد:
1 |
ValueError: The argument must have five elements |
سپس می توانید نحوه استفاده از استثناء را با امتحان کردن انتخاب کنید … به جز بیانیه. شما می توانید یک بند دیگر یا یک بند آخر اضافه کنید. شما می توانید آن را متناسب با نیاز خود تنظیم کنید.