فصل ششم

تا بحال چیزی در مورد پیغام خطاها گفته نشده است اما اگر شما مثال ها را تمرین کرده باشید ممکن است نمونه هایی را دیده باشید. حداقل ۲ نوع خطا مشخص شده است: خطاهای نحوی syntaxError و استثناها Exceptions

 خطاهای نحوی

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

>>> while True print('Hello world')
  File "<stdin>", line 1, in ?
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

در این نوع خطا خطی که خطا رخ داده بهمراه شماره ی آن خط در کد چاپ شده است شماره خط بما در یافتن محل وقوع خطا برای حل مشکل الزامی هستش و توی برنامه نویسی های بزرگ لازمه. خط آخر هم نوع خطا را یاد آوری کرده که یک خطای نحوی syntaxError بدلیل از قلم انداختن “:” برای حلقه while هستش

استثناها

حتی اگر یک جمله و یا عبارت از نظر نحوی درست باشد، ممکن است یک خطا به هنگام اجرا روی بدهد. خطاها تشخیص داده شده در حین اجرای  استثنا نامیده می شوند و بدون قید و شرط کشنده هستند . شما به زودی خواهد آموخت که چگونه آنها را در برنامه های پایتون اداره  و کنترل کنید . بیشتر استثنا ها در برنامه کنترل نشده اند، در پیغام خطا که در اینجا نشان داده شده است:
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: int division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly
آخرین خط پیغام خطا نشان می دهد چه اتفاقی افتاده است. استثناهای در انواع مختلف می باشند، و نوع آنها به عنوان بخشی از این پیام نمایش داده خواهد شد. انواع استثنا در مثال بالا نوع های زیر می باشند:  ZeroDivisionError، NameError,TypeError
 نوع استثنا ی رویداد داده  همچنین اسم استثنا نیز می باشد این برای تمام استثناهای توکار بکار گرفته می شود . همچنین می تواند برای استثناهای تعریف شده توسط کاربر نیز کنواسیون مناسب باشد .

اداره استثناها

امکان این نیز وجود دارد که استثناها را اداره کرد در زیر یه حلقه داریم که مکرر از کاربر درخواست وارد کردن یک عدد صحیح را می کند . طبیعتا اگر کاربر یک رشته یا عدد اعشاری را وارد کند استثنایی رخ می دهد و برنامه متوقف می شود اما ما در اینجا بدین صورت ان را اداره خواهیم کرد:
>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...

عبارت try اینگونه کار می کند:

  • نخست دستورات بین کلید واژه های try و except اجرا می شوند
  • اگر استثنایی روی ندهد except نادیده گرفته می شود و try پایان میپذیرد
  • اگر استثنایی در try روی بدهد اجراش متوف می شود و نام استثنا با نام پس از کلید واژه except مطابقت داده می شود اگر که یکی بودند دستورات بعد از except اجرا می شود و سپس ادامه برنامه پس از عبارت try دنبال می شود.

عبارت try ممکن است چندین except برای اداره ی استثناهای متفاوت داشته باشد تنها یک except می تواند اجرا شود و استثنای try را اداره کند.

یک except  می تواند چندین نام استثنا را بصورت یک تاپل پذیرا باشد:

... except (RuntimeError, TypeError, NameError):
...     pass

نام پس از except نیز می تواند نادیده گرفته شود در این صورت باید بسیار هوشیار باشید زیرا که این حالت می تواند خطاهای واقعی و جدی برنامه نویسی را بپوشاند و شما متوجه انها نشوید. همچنین می تواند برای چاپ کردن پیغام خطا و خود خطا برای استثناهایی شود که اداره نشده اند و نامی ندارند.

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as err:
    print("I/O error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

عبارت tryexcept  یک جزء اختیاری دیگر هم دارد بنام else که معمولا زیر تمام except ها تعریف می شود و وقتی که عبارت try استثنایی رو برپا نکند else اجرا می شود . برای مثال:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

بکار بردن else  بهتر از افزودن کدهای اضافی به عبارت try  هستس زیراکه از برخورد تصادفی با استثنایی که توسط try except برپا نشده جلو گیری می کند

وقتی استثنایی اتفاق می افتد  ممکن است مقادیری به آن در قالب آرگومان اختصاص داده شده باشد. حضور و نوع آرگومان بستگی به نوع استثنا دارد

همچنین امکان داردبه یک نمونه از استثنایی برای اولین بار قبل از برپایی ارگومان هایی یعنوان صفت داده شود :

 

>>> try:
...    raise Exception('spam', 'eggs')
... except Exception as inst:
...    print type(inst)     # the exception instance
...    print inst.args      # arguments stored in .args
...    print inst           # __str__ allows args to printed directly
...    x, y = inst.args
...    print 'x =', x
...    print 'y =', y
...

('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

کنترل کننده های خطا ،تنها خطای اتفاق افتاده بصورت مستقیم درون عبارت try را کنترل نمی کنند بلکه اگر خطا درون تابعی رخ بدهد کنترل کننده ها قادر به کنترل ان نیز هستند ( در تابع زیر سعی شده یک بر صفر تقسیم بشود):

>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as detail:
...     print 'Handling run-time error:', detail
...
Handling run-time error: integer division or modulo by zero

برپا کردن استثنا

عبارت raise  به برنامه نویس اجازه می دهد که استثنای مشخصی رو برپا کند برای مثال:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "", line 1, in ?
NameError: HiThere

اگر می خواهید مشخص کنید که استثنایی اتفاق افتاده ولی نمی خواهید ان را کنترل کنید می توانید دوبار ان را برپا  ( re-raise ) کنید:

>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print 'An exception flew by!'
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "", line 2, in ?
NameError: HiThere

 

استثناهای تعریف شده کاربر

پیش نیاز این بخش ، دانستن در مورد کلاس هاست. نگران نباشید شما می توانید درباره کلاس ها در فصل بعد مفصل بخوانید

برنامه ها ممکنه استثناهای خودشون رو بسازند برای ساختن استنا  باید استثنای ما بظور مستقیم یا غیر مستقیم از کلاس Exception مشتق شده باشد. برای مثال:

>>> class MyError(Exception):
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)
...
>>> try:
...     raise MyError(2*2)
... except MyError as e:
...     print 'My exception occurred, value:', e.value
...
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
  File "", line 1, in ?
__main__.MyError: 'oops!'

کلاس های استثنا می نوانند تمام کارهایی که کلاس های دیگر می کنند را تعریف کنند اما اغلب این کلاس ها ساده نگه داشته می شوند. توصیه می شه بیشتر شامل صفت هایی باشند که فقط اطلاعاتی رو درباره خطای گرفته شده توسط کنترل کننده استثنا نگهداری کنند.مثال:

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expr -- input expression in which the error occurred
        msg  -- explanation of the error
    """

    def __init__(self, expr, msg):
        self.expr = expr
        self.msg = msg

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        prev -- state at beginning of transition
        next -- attempted new state
        msg  -- explanation of why the specific transition is not allowed
    """

    def __init__(self, prev, next, msg):
        self.prev = prev
        self.next = next
        self.msg = msg

بیشنر استثناها با اسامی تعریف می شوند که آخرشون Error هست.  درست شبیه اسامی استثناهای استاندارد. مازول های زیادی برای رخ دادن خظاها از استثناهای تعریف شده ی خودشون استفاده می کنند. برای یادگیری در مورد کلاس ها به فصل ۷ مراجعه کنید.

گرداننده پاک سازی

عبارت try عبارت دیگری ر ا تعریف می کند که تحت هر شرایطی باید اجرا بشود. برای مثال:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print 'Goodbye, world!'
...
Goodbye, world!
KeyboardInterrupt

عبارت finally  همیشه قبل از خروج try اجرا می شود چه استثنایی اتفاق بیافتد و چه نیافتد. اگر استثنایی درون عبارت try  اتفاق بیافتد و توسط except  کنترل نشود پس از اجرای عبارت finally  دوباره ان خطا برپا خواهد شد. حتی اگر عبارت try توسط break , continue, return خارج شود عبارت finally اجرا خواهد شد. یک مثال پیچیده تر:

 

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print "division by zero!"
...     else:
...         print "result is", result
...     finally:
...         print "executing finally clause"
...
>>> divide(2, 1)
result is 2
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "", line 1, in ?
  File "", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

همین جور که می بینید عبارت finally  ذر هر شرایطی اجرا شده است. TypeError ناشی از تقسیم ۲ رشته بر هم است که توسط except  تقسیم بر صفر کنترل نمی شود. بعد از اجرای finally دوباره استثنای typeError برپا خواهد شد.

 

4 دیدگاه برای «فصل ششم»

  1. سلام من قسمت ۴ یه سریالی رو میخواستم ببینم زیرنویسشو درگ کردم و با این خطا مواجه شدم syntax error at line 806! در صورتی که منبا ورد زیرنویس رو باز کردم و خط۸۰۶ وجود ندارد یعنی فقط تا خط ۶۹۷ هست میشه راهنماییم کنید مشکل از کجاست ممنون میشم .

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *