استثناها

استثناها هنگامی رخ می دهند که در برنامۀ شما شرایطی استثنایی رخ دهد. برای مثال، اگر بخواهید فایلی را بخوانید، اما فایلی وجود نداشته باشد چه؟ یا اگر در حین اجرای برنامه، آن را به طور تصادفی حذف کرده باشید چه؟ چنین موقعیت هایی با استفاده از استثناها مدیریت می شوند.

به طور مشابه، اگر برنامۀ شما شامل دستوراتی نا معتبر باشد چه؟ در این حالت، پایتون این وضع را مدیریت می کند؛ یعنی به اصطلاح دست هایش را بالا می برد و به شما اعلام می کند که یک خطا رخ داده است.

خطاها (Errors)

یک فراخوانی ساده تابع print را در نظر بگیرید. اگر print را به اشتباه به صورت Print بنویسیم چه؟ به حروف بزرگ و کوچک توجه کنید. در این شرایط، پایتون یک خطای نحوی بالا می برد.

>>> Print("Hello World")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Hello World")
Hello World

همانطور که مشاهده می کنید، یک NameError ایجاد شده است و همچنین موقعیت تشخیص خطا نیز نمایش داده می شود. این همان کاری است که مدیریت کنندۀ خطا برای این خطا انجام می دهد.

استثناها

بیایید تلاش کنیم یک ورودی از کاربر دریافت کنیم. خط اول زیر را وارد کرده و کلید Enter را فشار دهید. هنگامی که رایانه از شما ورودی می خواهد، به جای وارد کردن چیزی در سیستم Mac کلیدهای [ctrl-d] و در windows کلیدهای [ctrl-z] را فشار دهید و ببینید چه اتفاقی می افتد. (اگر از ویندوز استفاده می کنید و هیچکدام از این کلیدها کار نکرد، می توانید در Command Prompt کلیدهای [ctrl-c] را فشار دهید تا به جای آن خطای KeyboardInterrupt ایجاد شود).

>>> s = input('Enter something --> ')
Enter something --> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EOFError

پایتون خطایی به نام EOFError ایجاد می کند که اساساً به این معناست که در جایی که انتظار نداشته با نماد ctrl-d مواجه شده است؛ این نماد انتهای فایل(end of file) را نشان می دهد.

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

می توانیم با استفاده از دستور try..except استثناها را مدیریت کنیم. دستورات معمول خود را در بلوک try قرار می دهیم و تمام مدیریت کننده های خطا را در بلوک except قرار می دهیم.

مثال را تحت عنوان exceptions_handle.py ذخیره کنید:

try:
    text = input('Enter something --> ')
except EOFError:
    print('Why did you do an EOF on me?')
except KeyboardInterrupt:
    print('You cancelled the operation.')
else:
    print('You entered {}'.format(text))

خروجی:

# Press ctrl + d
$ python exceptions_handle.py
Enter something --> Why did you do an EOF on me?

# Press ctrl + c
$ python exceptions_handle.py
Enter something --> ^CYou cancelled the operation.

$ python exceptions_handle.py
Enter something --> No exceptions
You entered No exceptions

نحوۀ عملکرد

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

توجه داشته باشید که هر بند try باید حداقل یک بند except مرتبط با خود داشته باشد. در غیر این صورت، اصلاً داشتن یک بلوک try چه فایده ای خواهد داشت؟

اگر هیچ خطا یا استثنایی مدیریت نشود، مدیریت کننده پیش فرض پایتون فراخوانی می شود؛ این مدیریت کننده اجرای برنامه را متوقف می کند و یک پیام خطا نمایش می دهد. نمونه ای از این عمل را بالاتر مشاهده کردیم.

همچنین می توانید یک بند else را به یک بلوک try..except مرتبط کنید. بند else زمانی اجرا می شود که هیچ استثنایی رخ ندهد.

در مثال بعد، خواهیم دید که چگونه می توانیم به شئ استثنا دسترسی پیدا کنیم تا اطلاعات بیشتری از آن به دست آوریم.

ایجاد استثناها

با استفاده از دستور raise می توانید استثنا ایجاد کنید. برای این کار، نام خطا یا استثنا یا شئ استثنایی را که قرار است پرتاب شود را مشخص می کنید.

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

مثال را با عنوان exception_raise.py ذخیره کنید:

class ShortInputException(Exception):
    '''A user-defined exception class.'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    text = input('Enter something --> ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
    # Other work can continue as usual here
except EOFError:
    print('Why did you do an EOF on me?')
except ShortInputException as ex:
    print(('ShortInputException: The input was ' +
           '{0} long, expected at least {1}')
          .format(ex.length, ex.atleast))
else:
    print('No exception was raised.')

خروجی:

$ python exceptions_raise.py
Enter something --> a
ShortInputException: The input was 1 long, expected at least 3

$ python exceptions_raise.py
Enter something --> abc
No exception was raised.

نحوۀ عملکرد

در اینجا نوعی استثنای مخصوص خودمان را ایجاد می کنیم. این نوع جدید از استثنا را ShortInputException می نامیم. این استثنا دارای دو فیلد است - length که طول ورودی داده شده است و atleast که حداقل طول مورد انتظار برنامه را مشخص می کند.

در بند except، کلاس خطایی را مشخص می کنیم که به عنوان نام متغیر ذخیره می شود تا شئ خطا یا استثنای مربوطه را نگه دارد. این کار مشابه پارامترها و آرگومان ها در فراخوانی یک تابع است. در بند except، از فیلدهای length و atleast شئ استثنا استفاده می کنیم تا پیام مناسبی برای کاربر نمایش دهیم.

Try ... Finally

فرض کنید درحال خواندن یک فایل در برنامه خود هستید. چگونه مطمئن شوید که شئ فایل چه استثنایی رخ داده باشد و چه رخ نداده باشد، به درستی بسته می شود؟ این کار را میتوان با استفاده از بلوک finally انجام دهیم.

این برنامه را با نام exception_finally.py ذخیره کنید:

import sys
import time

f = None
try:
    f = open("poem.txt")
    # Our usual file-reading idiom
    while True:
        line = f.readline()
        if len(line) == 0:
            break
        print(line, end='')
        sys.stdout.flush()
        print("Press ctrl+c now")
        # To make sure it runs for a while
        time.sleep(2)
except IOError:
    print("Could not find file poem.txt")
except KeyboardInterrupt:
    print("!! You cancelled the reading from the file.")
finally:
    if f:
        f.close()
    print("(Cleaning up: Closed the file)")

خروجی:

$ python exceptions_finally.py
Programming is fun
Press ctrl+c now
^C!! You cancelled the reading from the file.
(Cleaning up: Closed the file)

نحوۀ عملکرد

کارهای معمول مربوط به خواندن فایل را انجام می دهیم، اما عمداً بعد از پرینت هر خط، با استفاده از تابع time.sleep به مدت 2 ثانیه مکث ایجاد کرده ایم تا برنامه با سرعت کمتری ایجاد شود(پایتون ذاتاً بسیار سریع است). هنگامی که برنامه همچنان درحال اجراست، کلید ctrl + c را فشار دهید تا اجرای برنامه متوقف/لغو شود.

مشاهده می کنید که استثنای KeyboardInterrupt ایجاد شده و برنامه به پایان می رسد. با این حال، پیش از خروج برنامه، بند finally اجرا می شود و شئ فایل در هر صورت بسته خواهد شد.

توجه داشته باشید که متغیری که مقدار 0 یا None دارد یا متغیری که یک دنبالۀ یا مجموعه خالی از دیدگاه پایتون مقدار False در نظر گرفته می شود. به همین دلیل است که در کد بالا می توانیم از :if f استفاده کنیم.

همچنین توجه کنید که پس از print از ()sys.stdout.flush استفاده کردیم تا خروجی بلافاصله بر روی صفحه نمایش داده شود.

دستور with

به دست آوردن یک منبع در بلوک try و سپس آزادسازی آن منبع در بلوک finally یک الگوی رایج است. بنابراین، دستور with نیز وجود دارد که امکان انجام این کار را به شکلی ساده فراهم می کند:

با نام exceptions_using_with.py ذخیره کنید:

with open("poem.txt") as f:
    for line in f:
        print(line, end='')

نحوۀ عملکرد

خروجی باید با مثال قبلی یکسان باشد. تفاوت در اینجاست که از تابع open با دستور with استفاده می کنیم - بستن فایل را به with open می سپاریم تا به صورت خودکار انجام شود.

چیزی که در پشت صحنه اتفاق می افتد این است که دستور with از پروتکلی استفاده می کند. این دستور یک شئ را که دستور open بر می گرداند، دریافت می کند. در اینجا فرض می کنیم نام آن را "thefile" بگذاریم.

دستور with همیشه تابع _thefile._enter را پیش از شروع اجرای بلوک کد زیر خود را فراخوانی می کند و پس از پایان بلوک کد نیز همیشه تابع _thefile._exit را فراخوانی می کند.

بنابراین، کدی که در یک بلوک finally می نوشتیم، به طور خودکار توسط روش __exit__ مدیریت می شود. این ویژگی به ما کمک می کند تا مجبور نباشیم بارها از دستورات صریح try..finally استفاده کنیم.

بحث بیشتر دربارۀ این موضوع خارج از محدودۀ کتاب است، برای توضیح جامع تر لطفاً به PEP 343 مراجعه کنید.

جمع بندی

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

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

results matching ""

    No results matching ""