Skip to content

Commit 1f12500

Browse files
author
Saeid Darvish
committed
l21: Context Manager, completed
1 parent 2f07a3d commit 1f12500

1 file changed

Lines changed: 89 additions & 3 deletions

File tree

lessons/l21.rst

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ Context Manager و دستور ``with/as``
759759
with context_expression [as target]:
760760
with_statement_body
761761

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__>`__] را در کلاس مورد نظر خود پیاده‌سازی کنیم:
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__>`__] را در کلاس مورد نظر خود پیاده‌سازی کنیم:
763763

764764
.. code-block:: python
765765
:linenos:
@@ -786,9 +786,9 @@ Context Manager و دستور ``with/as``
786786

787787
اگر بخواهیم کمی عمیق‌تر به ماجرا نگاه کنیم:
788788

789-
* اجرای متد ``__enter__`` زمانی است که خط اجرای برنامه به اصطلاح می‌خواهد وارد runtime context شود و خروجی این متد می‌بایست شی‌ای باشد که می‌خواهیم در طول اجرای دستور ``with`` یا به اصطلاح context، با آن کار کنیم. البته خروجی می‌تواند ``None`` باشد ولی باید توجه داشت که خروجی این متد است که توسط دستور ``as`` به نام ``target`` ارجاع می‌خورد!
789+
* اجرای متد ``__enter__`` زمانی است که خط اجرای برنامه می‌خواهد وارد اجرای دستورات داخل ``with`` یا به اصطلاح وارد runtime context شود و خروجی این متد می‌بایست شی‌ای باشد که می‌خواهیم در طول اجرای دستور ``with`` یا به اصطلاح context، با آن کار کنیم. البته خروجی می‌تواند ``None`` باشد ولی باید توجه داشت که خروجی این متد است که توسط دستور ``as`` به نام ``target`` ارجاع می‌خورد!
790790

791-
* اجرای متد ``__exit__`` زمانی است که کار یا اجرای context به پایان رسیده است. این متد در واقع فرصتی برای تمیزکاری یا به اصطلاح clean up کردن آثار اجرای context می‌باشد. به مانند پاک کردن فایل‌هایی که موقت ایجاد شده‌اند، حذف اشیای اضافی باقی‌مانده یا انجام عمل بستن یک فایل یا پایان دادن یک ارتباط (Connection) یا...
791+
* اجرای متد ``__exit__`` زمانی است که انجام کار دستورات ``with`` یا اجرای context به پایان رسیده است. این متد در واقع فرصتی برای تمیزکاری یا به اصطلاح clean up کردن آثار اجرای context می‌باشد. به مانند پاک کردن فایل‌هایی که موقت ایجاد شده‌اند، حذف اشیای اضافی باقی‌مانده یا انجام عمل بستن یک فایل یا پایان دادن یک ارتباط (Connection) یا...
792792

793793
برای آشنایی بیشتر در نمونه کد زیر یک Wrapper برای شی فایل ایجاد کرده‌ایم:
794794

@@ -821,6 +821,92 @@ Context Manager و دستور ``with/as``
821821
Inside context manager!
822822
====== CLOSE FILE ======
823823

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+
824910

825911
|
826912

0 commit comments

Comments
 (0)