|
1 | 1 | .. role:: emoji-size |
2 | 2 |
|
3 | 3 | .. meta:: |
4 | | - :description: کتاب آموزش زبان برنامه نویسی پایتون به فارسی، آموزش شی گرایی در پایتون، OOP در پایتون، Decorators در پایتون، Descriptors در پایتون، property@ در پایتون |
| 4 | + :description: کتاب آموزش زبان برنامه نویسی پایتون به فارسی، آموزش شی گرایی در پایتون، OOP در پایتون، Decorators در پایتون، Descriptors در پایتون، property@ در پایتون، Context Manager در پایتون، دستور with / as |
5 | 5 | :keywords: آموزش, آموزش پایتون, آموزش برنامه نویسی, پایتون, Decorators, کتابخانه, پایتون, شی گرایی در پایتون, Descriptors,property@ |
6 | 6 |
|
7 | 7 |
|
8 | | -درس ۲۱: شی گرایی (OOP) در پایتون: __Descriptors ،Decorator ،__slots و property@ |
| 8 | +درس ۲۱: شی گرایی (OOP) در پایتون: Context Manager ،Descriptors ،Decorator |
9 | 9 | =================================================================================================== |
10 | 10 |
|
11 | 11 | .. figure:: /_static/pages/21-python-object-oriented-programming-property-descriptors.jpg |
12 | 12 | :align: center |
13 | | - :alt: شی گرایی (OOP) در پایتون: __Descriptors ،Decorator ،__slots و property@ |
| 13 | + :alt: شی گرایی (OOP) در پایتون: __Descriptors ، Context Manager ،Decorator ،__slots و property@ |
14 | 14 |
|
15 | 15 | Photo by `Mathyas Kurmann <https://unsplash.com/photos/fb7yNPbT0l8>`__ |
16 | 16 |
|
@@ -745,6 +745,168 @@ property@ |
745 | 745 | .. tip:: |
746 | 746 |
|
747 | 747 | از ``property@`` تنها برای Instance Attributeها میتوان استفاده کرد. |
| 748 | + |
| 749 | + |
| 750 | +Context Manager و دستور ``with/as`` |
| 751 | +------------------------------------------ |
| 752 | + |
| 753 | +یکی دیگر از قابلیتهای کمتر شناخته شده در زبان برنامهنویسی پایتون، Context Manager میباشد [`اسناد پایتون <http://docs.python.org/3/library/stdtypes.html#typecontextmanager>`__]. با این حال اکثر برنامهنویسان پایتون به صورت مداوم از آن بهره میگیرند. اگر درس دهم را به یاد داشته باشیم، از دستور ``with/as`` برای کار با فایلها در پایتون استفاده میکردیم و شاهد راحتی و زیبایی کارها نسبت به قبل بودیم. در آن زمان تنها اشاره شد که شی فایل پایتون را میتوان با دستور ``with/as`` استفاده کرد چون این شی از قابلیت Context Manager پشتیبانی میکند. |
| 754 | + |
| 755 | +به صورت کلی Context Manager در زبان برنامهنویسی پایتون قابلیتی برای مدیرت منابع (فایلها، دیتابیس، ارتباط و سایر منابع) میباشد، منابعی که کار کردن با آنها همواره نیازمند عملیات ثابتی همچون باز (Open) و بسته (Close) - Start/Stop, Lock/Release, Change/Reset - کردن هستند. |
| 756 | + |
| 757 | +در این بخش میخواهیم به بررسی چگونگی ایجاد یک کلاس به همراه قابلیت Context Manager بپردازیم که در نهایت از اشیای آن بتوانیم در کنار دستور ``with/as`` استفاده نماییم. ابتدا اجازه دهید بار دیگر ساختار دستور ``with/as`` را بررسی نماییم:: |
| 758 | + |
| 759 | + with context_expression [as target]: |
| 760 | + with_statement_body |
| 761 | + |
| 762 | +در این ساختار بخش ``as`` اختیاری بوده و تنها زمانی که در داخل بدنه دستور ``with`` به شی تولید شده توسط ``context_expression`` نیاز داشته باشیم، استفاده میگردد؛ در این صورت یک ارجاع از شی مورد نیاز به نام دلخواه ``target`` ایجاد و در دسترس قرار میگیرد. ``context_expression`` نیز معرف یک شیای است که توانایی مدیریت یا handle کردن دو وضعیت «ورود به» (entry into) و «خروج از» (exit from) را داشته باشد. برای ایجاد همچین شیای میبایست دو متد خاص ``__enter__`` [`اسناد پایتون <https://docs.python.domainunion.de/3/reference/datamodel.html#object.__enter__>`__] و ``__exit__`` [`اسناد پایتون <https://docs.python.domainunion.de/3/reference/datamodel.html#object.__exit__>`__] را در کلاس مورد نظر خود پیادهسازی کنیم: |
| 763 | + |
| 764 | +.. code-block:: python |
| 765 | + :linenos: |
| 766 | + |
| 767 | + class SampleContextManager: |
| 768 | + def __enter__(self): |
| 769 | + print('---> Entered into context manager!') |
| 770 | +
|
| 771 | + def __exit__(self, *args): |
| 772 | + print('<--- Exiting from context manager!') |
| 773 | +
|
| 774 | +
|
| 775 | + with SampleContextManager(): |
| 776 | + print('Inside context manager!') |
| 777 | +
|
| 778 | +:: |
| 779 | + |
| 780 | + ---> Entered into context manager! |
| 781 | + Inside context manager! |
| 782 | + <--- Exiting from context manager! |
| 783 | + |
| 784 | +همانطوری که از خروجی نمونه کد بالا قابل مشاهده میباشد، در هنگام اجرای دستور ``with``، ابتدا متد ``__enter__`` از شی Context Manager و سپس دستورات داخل بدنه دستور ``with`` و در نهایت نیز متد ``__exit__`` از شی Context Manager اجرا میگردد. |
| 785 | + |
| 786 | + |
| 787 | +اگر بخواهیم کمی عمیقتر به ماجرا نگاه کنیم: |
| 788 | + |
| 789 | +* اجرای متد ``__enter__`` زمانی است که خط اجرای برنامه میخواهد وارد اجرای دستورات داخل ``with`` یا به اصطلاح وارد runtime context شود و خروجی این متد میبایست شیای باشد که میخواهیم در طول اجرای دستور ``with`` یا به اصطلاح context، با آن کار کنیم. البته خروجی میتواند ``None`` باشد ولی باید توجه داشت که خروجی این متد است که توسط دستور ``as`` به نام ``target`` ارجاع میخورد! |
| 790 | + |
| 791 | +* اجرای متد ``__exit__`` زمانی است که انجام کار دستورات ``with`` یا اجرای context به پایان رسیده است. این متد در واقع فرصتی برای تمیزکاری یا به اصطلاح clean up کردن آثار اجرای context میباشد. به مانند پاک کردن فایلهایی که موقت ایجاد شدهاند، حذف اشیای اضافی باقیمانده یا انجام عمل بستن یک فایل یا پایان دادن یک ارتباط (Connection) یا... |
| 792 | + |
| 793 | +برای آشنایی بیشتر در نمونه کد زیر یک Wrapper برای شی فایل ایجاد کردهایم: |
| 794 | + |
| 795 | +.. code-block:: python |
| 796 | + :linenos: |
| 797 | + |
| 798 | + class FileWritterWrapper: |
| 799 | + def __init__(self, filename): |
| 800 | + self.filename = filename |
| 801 | + |
| 802 | + def __enter__(self): |
| 803 | + self.opened_file = open(self.filename, 'a') |
| 804 | + self.opened_file.write('====== OPEN FILE ======\n') |
| 805 | + return self.opened_file |
| 806 | + |
| 807 | + def __exit__(self, *args): |
| 808 | + self.opened_file.write('\n====== CLOSE FILE ======\n') |
| 809 | + self.opened_file.close() |
| 810 | +
|
| 811 | +
|
| 812 | + with FileWritterWrapper('test_log.txt') as managed_file: |
| 813 | + managed_file.write('Inside context manager!') |
| 814 | +
|
| 815 | +محتویات فایل test_log.txt، پس از اجرای کد بالا: |
| 816 | + |
| 817 | +:: |
| 818 | + |
| 819 | + |
| 820 | + ====== OPEN FILE ====== |
| 821 | + Inside context manager! |
| 822 | + ====== CLOSE FILE ====== |
| 823 | + |
| 824 | +به متد ``__exit__`` برگردیم، براساس مستندات پایتون تعریف کامل این متد به شکل زیر است:: |
| 825 | + |
| 826 | + __exit__(self, exc_type, exc_value, traceback) |
| 827 | + |
| 828 | +سه پارامتر انتهایی در صورت بروز Exception هنگام اجرای context (دستورات داخل بدنه ``with``) دارای مقدار غیر ``None`` و در غیر این صورت برابر با مقدار ``None`` خواهند بود. وجود این مقادیر به معنی عدم پایان صحیح context میباشد که ممکن است بتواند در گرفتن تصمیم شما در زمان خروج از context تاثیر داشته باشد. |
| 829 | + |
| 830 | +.. code-block:: python |
| 831 | + :linenos: |
| 832 | +
|
| 833 | + class SampleContextManager: |
| 834 | + def __enter__(self): |
| 835 | + print('---> Entered into context manager!') |
| 836 | +
|
| 837 | + def __exit__(self, exc_type, exc_value, traceback): |
| 838 | + print('exc_type:', exc_type) |
| 839 | + print('exc_value:', exc_value) |
| 840 | + print('traceback:', traceback) |
| 841 | + print('<--- Exiting from context manager!') |
| 842 | +
|
| 843 | +
|
| 844 | + with SampleContextManager(): |
| 845 | + print('|||||||||Inside context manager! - Top') |
| 846 | + a = 8 / 0 |
| 847 | + print('|||||||||Inside context manager! - Bottom') |
| 848 | +
|
| 849 | +
|
| 850 | + print('***FINISH***') |
| 851 | +
|
| 852 | +:: |
| 853 | + |
| 854 | + ---> Entered into context manager! |
| 855 | + |||||||||Inside context manager! - Top |
| 856 | + exc_type: <class 'ZeroDivisionError'> |
| 857 | + exc_value: division by zero |
| 858 | + traceback: <traceback object at 0x7f1c8aebd0c8> |
| 859 | + <--- Exiting from context manager! |
| 860 | + Traceback (most recent call last): |
| 861 | + File "sample.py", line 14, in <module> |
| 862 | + a = 8 / 0 |
| 863 | + ZeroDivisionError: division by zero |
| 864 | + |
| 865 | + |
| 866 | +همانطور که از نمونه کد بالا قابل مشاهده است، در زمان اجرای دستورات context یک خطای (تقسیم بر صفر) ``ZeroDivisionError`` رخ داده است. نکته قابل توجه این است که حتی با وجود بروز خطا و ناتمام ماندن اجرای context، ولی بدنه متد ``__exit__`` به صورت کامل اجرا شده است. در واقع مفسر پایتون اعلام Exception را که میتواند منجر به توقف کل برنامه شود را به صورت موقت تا پایان اجرا ``__exit__`` معلق نگه میدارد. |
| 867 | + |
| 868 | +در چنین حالتی اگر متد ``__exit__`` مقدار ``True`` را برگرداند، مفسر پایتون از بروز Exception خودداری خواهد کرد: |
| 869 | + |
| 870 | +.. code-block:: python |
| 871 | + :linenos: |
| 872 | +
|
| 873 | + class SampleContextManager: |
| 874 | + def __enter__(self): |
| 875 | + print('---> Entered into context manager!') |
| 876 | +
|
| 877 | + def __exit__(self, exc_type, exc_value, traceback): |
| 878 | + print('exc_type:', exc_type) |
| 879 | + print('exc_value:', exc_value) |
| 880 | + print('traceback:', traceback) |
| 881 | + print('<--- Exiting from context manager!') |
| 882 | + return True |
| 883 | +
|
| 884 | +
|
| 885 | + with SampleContextManager(): |
| 886 | + print('|||||||||Inside context manager! - Top') |
| 887 | + a = 8 / 0 |
| 888 | + print('|||||||||Inside context manager! - Bottom') |
| 889 | +
|
| 890 | +
|
| 891 | + print('***FINISH***') |
| 892 | +
|
| 893 | +:: |
| 894 | + |
| 895 | + ---> Entered into context manager! |
| 896 | + |||||||||Inside context manager! - Top |
| 897 | + exc_type: <class 'ZeroDivisionError'> |
| 898 | + exc_value: division by zero |
| 899 | + traceback: <traceback object at 0x7f4b3d520048> |
| 900 | + <--- Exiting from context manager! |
| 901 | + ***FINISH*** |
| 902 | + |
| 903 | +یادآوری: میدانیم که خروجی هر تابع یا متد به صورت پیشفرض برای ``None`` میباشد و این مقدار در مقام ارزشسنجی بولین، ارزشی برابر با مقدار ``False`` دارد. |
| 904 | + |
| 905 | +*در طی دروس آینده به مبحث Exception و مدیریت آن خواهیم پرداخت.* |
| 906 | + |
| 907 | + |
| 908 | + |
| 909 | + |
748 | 910 |
|
749 | 911 | | |
750 | 912 |
|
|
0 commit comments