|
5 | 5 | :keywords: آموزش, آموزش پایتون, آموزش برنامه نویسی, پایتون, Data Class, کتابخانه, پایتون, شی گرایی در پایتون |
6 | 6 |
|
7 | 7 |
|
8 | | -درس ۲۳: مدیریت خطا در پایتون: Exception ،Error و Warning |
| 8 | +درس ۲۳: مدیریت خطا در پایتون: Exception ،Traceback و Exception Handling |
9 | 9 | =================================================================================================== |
10 | 10 |
|
11 | 11 | .. figure:: /_static/pages/23-python-exception-error-warning-try.jpg |
|
171 | 171 | این نکته را نیز در نظر بگیرید - همانطور که اگر به خروجیهای دقت کرده باشید حتما متوجه شدهاید در دو حالت مربوط به گزارش خطای مربوط به ``SyntaxError`` خبری از سطر ``:Traceback (most recent call last)`` که در حالت خطای زمان اجرای ``TypeError`` مشاهده کردیم، نمیباشد. در واقع این سطر تنها در گزارش خطاهایی که پس از اجرای برنامه رخ دهند (Runtime errors)، نمایش داده خواهد شد. در زمان بررسی و ترجمه کد پایتون به بایتکد هرجا مشکلی باشد عملیات در همان نقطه متوقف میشود و صرفا گزارشی مبنی بر ابراز آن نقطه به برنامهنویس ارايه میگردد و نه چیزی که بتوان آن را یک گزارش ردیابی با Traceback نامید چرا که هنوز برنامه به اجرا درنیامده و اصلا نیازی به این کار نیست! |
172 | 172 |
|
173 | 173 |
|
174 | | -Exception handling |
175 | | -~~~~~~~~~~~~~~~~~~~~~~~~ |
| 174 | +مدیریت خطا (Exception Handling) |
| 175 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 176 | + |
176 | 177 | در زبانهای برنامهنویسی صدای اعتراض یک Exception قابل درک و تشخیص است و میتوان برای آنها فرآیندی را پیشبینی کرد که بروز آنها نه تنها باعث اتمام برنامه نشود بلکه برنامه بتواند در مسیر درست به اجرای خود ادامه دهد. |
177 | 178 |
|
178 | 179 | در زبان برنامهنویسی پایتون دستور ``try/except`` برای همین منظور فراهم دیده شده است [`اسناد پایتون <https://docs.python.org/3/reference/compound_stmts.html#the-try-statement>`__]. |
@@ -474,6 +475,36 @@ Exception handling |
474 | 475 | Successful, closed! |
475 | 476 |
|
476 | 477 |
|
| 478 | +**توجه داشته باشید،** چنانچه بدنه ``try`` شامل دستور ``return`` باشد، آنگاه بدنه دستور ``else`` اجرا نخواهد شد!: |
| 479 | + |
| 480 | + |
| 481 | +.. code-block:: python |
| 482 | + :linenos: |
| 483 | +
|
| 484 | + def print_sum_div_first(a, b): |
| 485 | + try: |
| 486 | + sum = a + b |
| 487 | + div = sum / a |
| 488 | + return 'Successful' |
| 489 | +
|
| 490 | + except Exception as err: |
| 491 | + return 'Failed' |
| 492 | +
|
| 493 | + else: |
| 494 | + return 'Successful, from else!' |
| 495 | +
|
| 496 | + result = print_sum_div_first(5, 6) # Successful |
| 497 | + print(result) |
| 498 | +
|
| 499 | + result = print_sum_div_first(3, 'G') # Failed |
| 500 | + print(result) |
| 501 | +
|
| 502 | +:: |
| 503 | + |
| 504 | + Successful |
| 505 | + Failed |
| 506 | + |
| 507 | + |
477 | 508 |
|
478 | 509 | ``try/finally`` ``try/except/finally`` ``try/except/else/finally`` |
479 | 510 | --------------------------------------------------------------------------------------------- |
@@ -522,9 +553,158 @@ Exception handling |
522 | 553 | ----> except |
523 | 554 | ----> finally |
524 | 555 |
|
525 | | -کاربرد اصلی دستور ``finally`` تمیزکاری یا Cleaning Up کردن کد پس از انجام کاری مشخص است. |
| 556 | +حتی اگر زمانی داخل بدنه دستور ``except`` نیز یک Exception رخ دهد، مفسر پایتون اعلام اعتراض آن Exception را موقتا نگهمیدارد تا بدنه دستور ``finally`` به صورت کامل اجرا گردد. در واقع کاربرد اصلی دستور ``finally`` - که تحت هر شرایطی اجرا میگردد - تمیزکاری یا Cleaning Up کردن کد پس از انجام کاری مشخص است (پاک کردن فایلهای موقت، آزادسازی منابع، حذف اشیایی که دیگر مورد نیاز نیستند و...) که از آن معمولا به عنوان Cleanup Handler نیز یاد میشود: |
| 557 | + |
| 558 | + |
| 559 | +.. code-block:: python |
| 560 | + :linenos: |
| 561 | +
|
| 562 | + def print_sum_div_first(a, b): |
| 563 | + try: |
| 564 | + sum = a + b |
| 565 | + div = sum / a |
| 566 | +
|
| 567 | + except TypeError as err: |
| 568 | + print(f'{err.__class__.__name__}: ({a}+{b!r})/{a} =', (a+b)/a) |
| 569 | +
|
| 570 | + finally: |
| 571 | + print('----> finally') |
| 572 | +
|
| 573 | +
|
| 574 | + print_sum_div_first(5, 6) |
| 575 | + print('*' * 20) |
| 576 | + print_sum_div_first(3, 'G') |
| 577 | +
|
| 578 | +:: |
| 579 | + |
| 580 | + ----> finally |
| 581 | + ******************** |
| 582 | + ----> finally |
| 583 | + Traceback (most recent call last): |
| 584 | + File "sample.py", line 3, in print_sum_div_first |
| 585 | + sum = a + b |
| 586 | + TypeError: unsupported operand type(s) for +: 'int' and 'str' |
| 587 | + |
| 588 | + During handling of the above exception, another exception occurred: |
| 589 | + |
| 590 | + Traceback (most recent call last): |
| 591 | + File "sample.py", line 15, in <module> |
| 592 | + print_sum_div_first(3, 'G') |
| 593 | + File "sample.py", line 7, in print_sum_div_first |
| 594 | + print(f'{err.__class__.__name__}: ({a}+{b!r})/{a} =', (a+b)/a) |
| 595 | + TypeError: unsupported operand type(s) for +: 'int' and 'str' |
| 596 | + |
| 597 | +همانطور که از خروجی نمونه کد بالا مشاهده میشود، داخل بدنه دستور ``except``، یک Exception دیگر رخ داده است. نکته قابل توجه این است که حتی در این وضعیت نیز بدنه دستور ``finally`` اجرا شده و سپس وقوع Exception بدنه ``except`` باعث توقف برنامه شده است. |
| 598 | + |
| 599 | +اگر به گزارش Traceback پایتون در این وضعیت دقت نمایید، مشاهده خواهید کرد که این گزارش چقدر کامل است چرا که حتی به ما میگوید در هنگام handle کردن یک Exception بوده که Exception دیگری رخ داده است! |
| 600 | + |
| 601 | + |
| 602 | +**توجه داشته باشید،** چنانچه بدنه ``try`` و ``except`` و ``finally`` شامل دستور ``return`` باشند، آنگاه این دستور ``return`` از بدنه دستور ``finally`` است که اجرا خواهد شد!: |
| 603 | + |
| 604 | + |
| 605 | +.. code-block:: python |
| 606 | + :linenos: |
| 607 | +
|
| 608 | + def print_sum_div_first(a, b): |
| 609 | + try: |
| 610 | + sum = a + b |
| 611 | + div = sum / a |
| 612 | + return 'Successful' |
| 613 | +
|
| 614 | + except Exception as err: |
| 615 | + return 'Failed' |
| 616 | +
|
| 617 | + else: |
| 618 | + return 'Successful, from else!' |
| 619 | +
|
| 620 | + finally: |
| 621 | + return '---------->finally!' |
| 622 | +
|
| 623 | + result = print_sum_div_first(5, 6) # Successful |
| 624 | + print(result) |
| 625 | +
|
| 626 | + result = print_sum_div_first(3, 'G') # Failed |
| 627 | + print(result) |
| 628 | +
|
| 629 | +:: |
| 630 | + |
| 631 | + ---------->finally! |
| 632 | + ---------->finally! |
| 633 | + |
| 634 | + |
| 635 | +گاهی تنها از دستور ``finally`` در کنار ``try`` استفاده میگردد، یعنی بدون حضور هیچگونه دستور ``except`` به صورت ``try/finally``. میتوان از این قالب برای زمانیکه رخداد Exception و مدیریت آن برایمان اهمیتی نداشته باشد، بهره بگیریم. با این حال به نمونه کد زیر توجه نمایید: |
| 636 | + |
| 637 | + |
| 638 | +.. code-block:: python |
| 639 | + :linenos: |
| 640 | +
|
| 641 | + def print_sum_div_first(a, b): |
| 642 | + try: |
| 643 | + sum = a + b |
| 644 | + div = sum / a |
| 645 | + print(f'----> Result: {div}') |
| 646 | +
|
| 647 | + finally: |
| 648 | + print('--------> Finished!') |
| 649 | +
|
| 650 | +
|
| 651 | + print_sum_div_first(5, 6) |
| 652 | + print('*' * 30) |
| 653 | + print_sum_div_first(3, 'G') |
| 654 | +
|
| 655 | +:: |
| 656 | + |
| 657 | + ----> Result: 2.2 |
| 658 | + --------> Finished! |
| 659 | + ****************************** |
| 660 | + --------> Finished! |
| 661 | + Traceback (most recent call last): |
| 662 | + File "sample.py", line 13, in <module> |
| 663 | + print_sum_div_first(3, 'G') |
| 664 | + File "sample.py", line 3, in print_sum_div_first |
| 665 | + sum = a + b |
| 666 | + TypeError: unsupported operand type(s) for +: 'int' and 'str' |
| 667 | + |
| 668 | +به هر حال Exception بدون handler باعث توقف اجرای برنامه میشود اما اگر داخل بدنه ``finally`` شامل دستور ``return`` باشد، آنگاه مفسر پایتون از اعلام Exception رخ داده که در حال حاظر به صورت موقت نگهداشته است تا اجرای بدنه ``finally`` به پایان برسد، صرف نظر خواهد کرد!: |
| 669 | + |
| 670 | +.. code-block:: python |
| 671 | + :linenos: |
| 672 | +
|
| 673 | + def print_sum_div_first(a, b): |
| 674 | + try: |
| 675 | + sum = a + b |
| 676 | + div = sum / a |
| 677 | + print(f'----> Result: {div}') |
| 678 | +
|
| 679 | + finally: |
| 680 | + print('--------> Finished!') |
| 681 | + return None |
| 682 | +
|
| 683 | +
|
| 684 | + print_sum_div_first(5, 6) |
| 685 | + print('*' * 30) |
| 686 | + print_sum_div_first(3, 'G') |
| 687 | +
|
| 688 | +:: |
| 689 | + |
| 690 | + ----> Result: 2.2 |
| 691 | + --------> Finished! |
| 692 | + ****************************** |
| 693 | + --------> Finished! |
| 694 | + |
| 695 | + |
| 696 | +مدیریت خطای تودرتو (Nested Exception Handling) |
| 697 | +--------------------------------------------------- |
| 698 | + |
| 699 | +به صورت کلی بدنه هر یک از دستورهای ``try`` ، ``except`` ، ``else`` ، ``finally`` به خودیخود میتوانند شامل یک دستور ``try/except/else/finally`` دیگر باشد. هر جا که کدی نوشته شود آنجا نیز احتمال بروز Exception وجود دارد و هر جا که احتمال بروز Exception وجود داشته باشد به یک handler برای آن نیاز است. |
| 700 | + |
| 701 | +البته از آنجا که در یکی از بندهای فلسفه پایتون آمده: `PEP 20: Flat is better than nested <https://www.python.org/dev/peps/pep-0020/>`__ انجام اینکار چندان پایتونی نمیباشد و برنامهنویس احتمالا میتواند با کمی دقت بیشر از ساختار تودرتو پرهیز کند و کدی به مراتب خواناتر توسعه دهد. به هر حال امکان این کار در زبان برنامهنویسی پایتون برای برنامهنویس محفوظ نگهداشته شده است. |
| 702 | + |
| 703 | + |
526 | 704 |
|
527 | 705 |
|
| 706 | +Exception Hierarchy |
| 707 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
528 | 708 |
|
529 | 709 |
|
530 | 710 |
|
|
0 commit comments