در سالهای اخیر، پایتون به دلیل وجود ویژگیها و کتابخانههای کاربردی به یکی از محبوبترین زبانهای برنامه نویسی تبدیل شده است. علاوه بر این، این زبان برنامه نویسی همهمنظوره، حاوی عبارتهای (Statement) خاصی است که کدنویسی با کمک آنها به میزان زیادی سادهتر میشود. در همین راستا، عبارت With در پایتون یکی از دستورات پر اهمیت به حساب میآید که با کمک آن میتوان کارکردهای مختلفی را پیادهسازی کرد. در این مقاله ابتدا به آموزش With در پایتون پرداخته میشود و در ادامه، موارد استفاده این عبارت (With Statement) به زبان ساده و کاربردی شرح داده خواهند شد.
With در پایتون چیست و چه کاربردی دارد؟
عبارت With در پایتون ابزاری کارآمد برای مدیریت صحیح منابع خارجی در برنامه به حساب میآید. آموزش With در پایتون به برنامه نویسان اجازه میدهد تا از Context Managerهای موجود برای مدیریت خودکار مراحل «راهاندازی» (Setup) و «تخریب» (Teardown) هر زمانی استفاده کنند که با منابع خارجی سر و کار دارند یا با عملیاتی سر و کار دارند که نیازمند آن مراحل هستند. علاوه بر این، پروتکل مدیریت زمینه به برنامه نویسان امکان میدهد تا Context Managerهای خود را بسازند.
بدین سبب آنها میتوانند نحوه کار با منابع سیستم را شخصیسازی کنند. حال این سوال به وجود میآید که عبارت With در پایتون چه فایدهای دارد؟ عبارت With در پایتون به برنامه نویسان کمک میکند تا برخی از الگوهای رایج مدیریت منابع (Resource Management) را به وسیله انتزاعی کردن کارکرد آنها و فراهم کردن امکان کنار گذاشتن و استفاده مجدد از آنها پیادهسازی کنند. حال در ادامه برای روشنتر شدن موضوع به بیشتر به بحث مدیریت منابع در پایتون پرداخته شده است.
مدیریت منابع در پایتون
معمولاً یکی از رایجترین چالشهایی که برنامه نویسان با آن مواجه هستند، مدیریت منبعهای خارجی مانند فایلها، قفلها و اتصالات شبکه است. در برخی مواقع، همه این منابع در یک برنامه برای همیشه نگهداری میشوند. این در حالی است که این برنامه اغلب حتی به این منابع نیازی ندارد. چنین شرایطی اصطلاحاً با نام «نشت حافظه« (Memory Leak) شناخته میشود. در این شرایط، حافظه قابل دسترس کاهش مییابد، چون هر بار نمونه جدیدی از یک منبع خاص بدون بستن منبع موجود، ایجاد و باز میشود.
معمولاً مدیریت صحیح منابع یک موضوع پیچیده است. چرا که، این عمل هم به مرحله راهاندازی و هم به مرحله تخریب نیاز دارد. در مرحله تخریب باید برخی اقدامات مربوط به پاکسازی مانند بستن فایل، آزاد کردن قفل یا بستن اتصال شبکه را انجام داد. در صورتی که اقدامات مربوط به پاکسازی فراموش شوند، منابع در آن برنامه به صورت برخط حفظ خواهند شد و به دنبال آن، ممکن است منابع ارزشمند سیستم مانند حافظه و پهنای باند شبکه در معرض خطر قرار بگیرند.
چرا نیاز به مدیریت منابع وجود دارد؟
به عنوان مثال، هنگامی که توسعهدهندگان مشغول کار با پایگاه داده (Database | DB) هستند با چالشهای مختلفی رو به رو میشوند. مثلاً در شرایطی که درون یک برنامه اتصالات جدید به طور دائم و بدون آزادسازی یا استفاده مجدد از آنها ایجاد میشوند، در بک اند (Back End) پایگاه داده دیگر عمل پذیرش اتصالات جدید متوقف خواهد شد. در بیشتر موارد، به منظور استفاده مجدد از پایگاه داده، باید مدیر (Admin) وارد سیستم شود و به صورت دستی آن اتصالات قدیمی را از بین ببرد.
یکی دیگر از مشکلات پرتکرار مربوط به مدیریت منابع هنگام کار با فایلها رخ میدهد. معمولاً نوشتن متن درون فایلها یک عملیات بافر شده (Buffered) است. منظور از عملیات بافر شده این است که فراخوانی متُد ()write. در یک فایل منجر به نوشتن سریع متن در فایل فیزیکی نمیشود. بلکه با فراخوانی ()write. در فایل، متن مربوطه تنها در بافر موقت قرار میگیرد. در برخی مواقع، در صورتی که بافر کاملاً پر شده باشد و برنامه نویس فراخوانی متُد close(). را فراموش کند، ممکن است بخشی از دادهها برای همیشه از دست بروند.
یکی از سناریوهای دیگر که امکان رخداد آن وجود دارد، خطاها و استثناهای اپلیکیشنها هستند. گاهی این خطاها و استثناها باعث میشوند که جریان کنترلی از کدهای مربوط به آزادسازی منابع عبور کند. در چنین شرایطی، لازم است از متُد «()Open.» برای نوشتن متن در یک فایل استفاده شود. برای درک بهتر باید به قطعه کد زیر توجه شود:
file = open("hello.txt", "w")
file.write("Hello, World!")
file.close()
پیادهسازی قطعه کد فوق تضمین نمیکند که فایل در صورت رخداد یک استثنا در طول فراخوانی ()write. بسته خواهد شد. در چنین مواردی، هرگز متُد ()close. فراخوانی نمیشود. به همین دلیل ممکن است در این برنامه نشت توصیفگر فایل (File Descriptor) به وجود بیاید. به طور کلی، در پایتون دو رویکرد برای رسیدگی به منابع و مدیریت آن وجود دارد:
- استفاده از Try – Finally
- استفاده از With
در ادامه آموزش with در پایتون ، هر یک از دو رویکرد فوق بررسی شدهاند.
رویکرد استفاده از Try – Finally در پایتون
Try – Finally یک روش تقریباً کلی است و به برنامه نویسان این امکان را میدهد که مراحل راهاندازی و تخریب برای هر نوع منبعی قابل انجام باشد. البته باید توجه کرد که اگر در این روش اقدامات مربوط به پاکسازی فراموش شوند، مشکلات بسیاری ایجاد خواهند شد.
تقریباً استفاده از رویکرد Try – Finally رایجترین مثال برای مدیریت منابع در برنامه نویسی به حساب میآید. میتوان از عبارت Try – Finally در پایتون به منظور مدیریت صحیح باز و بسته کردن فایلها استفاده کرد. برای رسیدگی به اعمالی مانند باز و بسته کردن فایلها با عبارت Try – Finally، باید از کدهای زیر استفاده کرد:
# Safely open the file
file = open("hello.txt", "w")
try:
file.write("Hello, World!")
finally:
# Make sure to close the file after using it
file.close()
در قطعه کد فوق، لازم است فایل متنی hello.txt به صورت ایمن باز شود. این عمل با فراخوانی ()open در عبارت Try – Finally امکانپذیر است. در ادامه، در صورتی که عملیات نوشتن در این فایل انجام شود، گزاره finally تضمیندهنده این مسئله است که فایل به درستی بسته خواهد شد. لازم به ذکر است که در این شرایط حتی اگر یک استثنا در طول فراخوانی ()Write. در بخش Try رخ دهد، باز هم فایل مربوطه به درستی بسته خواهد شد. میتوان از این الگو به منظور مدیریت مراحل راهاندازی و تخریب در هنگام مدیریت منابع خارجی در پایتون استفاده کرد.
در مثال بالا، ممکن است درون بلوک Try استثناهایی مانند خطای صفت (AttributeError) یا خطای نام (NameError) رخ دهند. مشابه قطعه کد زیر، میتوان رسیدگی به این استثناها و مدیریت آنها را در یک گزاره Except انجام داد:
# Safely open the file
file = open("hello.txt", "w")
try:
file.write("Hello, World!")
except Exception as e:
print(f"An error occurred while writing to the file: {e}")
finally:
# Make sure to close the file after using it
file.close()
با کمک کدهای فوق، استثناهای احتمالی گوناگونی پیدا میشوند که امکان وقوع آنها وجود دارد. در مثالهای واقعی برای پیشگیری از رد شدن از خطاهای ناشناخته، باید به جای ایجاد یک Exception کلی، از یک نوع استثنا خاص استفاده کرد.
رویکرد استفاده از With در پایتون
با استفاده از عبارت With در پایتون اعمالی مانند ارائه، راهاندازی مجدد و تخریب کدها تسهیل میشود. باید توجه کرد که با به کارگیری عبارت With در پایتون میتوان تنها با Context Managerها کار کرد. به بیان ساده، عبارت With در پایتون یک «زمینه زمان اجرا» (Runtime Context) ایجاد میکند که به واسطه آن میتوان گروهی از عبارتها را تحت کنترل Context Manager اجرا کرد. با اضافه کردن PEP 343 به عبارت With، امکان کنار گذاشتن موارد استفاده استاندارد از عبارت Try … Finally وجود دارد.
در مقایسه با رویکرد سنتی ساختارهای Try … Finally، استفاده از عبارت With در پایتون میتواند به تمیزتر شدن، ایمنتر شدن و قابل استفادهتر شدن کدها منجر شود. در کتابخانههای استاندارد، پشتیانی از عبارت With در کلاسهای متعددی وجود دارد. یکی از مثال های مرسوم آن ()Open است که با به کارگیری آن همراه با With میتوان با اشیای فایل کار کرد. به منظور نوشتن یک عبارت With در پایتون، باید از سینتکس کلی زیر استفاده کرد:
with expression as target_var:
do_something(target_var)
شی Context Manager با ارزیابی Expression (اظهار) بعد از With حاصل میشود. در حقیقت، این عبارت باید شی خاصی را بازگرداند که در آن پروتکل مدیریت فضا پیادهسازی میشود. این پروتکل دارای دو متد (Method) مخصوص است:
- ()__enter__.: این متد برای ورود به فضای زمان اجرا در عبارت With فراخوانی میشود.
- ()__exit__.: این متد زمانی فراخوانی میشود که اجرای بلوک کد With به اتمام میرسد.
لازم به ذکر است که Specifier (شناساگر) as انتخابی است. در صورتی که target_var همراه با as به کار برود، مقدار بازگشتی از فراخوانی ()__enter__. در شی Context Manager به آن متغیر محدود میشود.
عملکرد With در پایتون چگونه است؟
زمانی که عبارت With در پایتون اجرا میشود، اعمال زیر رخ میدهند:
- فراخوانی Expression (اظهار) به منظور در اختیار گرفتن Context Manager
- ذخیرهسازی متدهای ()__enter__. و ()__exit__. مربوط به Context Manager برای استفاده در آینده
- فراخوانی ()__enter__. در Context Manager و در صورت لزوم، نگاشت مقدار بازگشتی در target_var
- اجرای بلوک کد مربوط به With
- فراخوانی ()__exit__. در Context Manager پس از اتمام اجرای بلوک کد With
در چنین مواردی، معمولاً متد ()__enter__. برای تنظیمات مربوط به کدها کاربرد دارد. عبارت With یک عبارت ترکیبی به حساب میآید که شروع کننده یک بلوک کد مانند یک دستور شرطی یا یک حلقه For است. در داخل چنین بلوک کدی میتوان عبارتهای مختلفی را اجرا کرد. به طور کلی، میتوان در صورت لزوم از بلوک کد With برای دستکاری target_var استفاده کرد. پس از اتمام بلوک کد With، متد ()__exit__. فراخوانی میشود. معمولاً با به کارگیری این روش، منطق تخریب یا پاکسازی کد مثل فراخوانی ()close. در یک شی فایل باز ارائه میشود. با توجه به سادگی به دست آوردن و آزادسازی منابع با استفاده از عبارت With در پایتون، میتوان گفت که به کارگیری آن بسیار مفید است. باز کردن و نوشتن در فایل متنی hello.txt با کمک عبارت With در پایتون ، به صورت زیر انجام میشود:
with open("hello.txt", mode="w") as file:
file.write("Hello, World!")
با اجرای عبارت With فوق، یک شی io.TextIOBase توسط ()Open بازگردانده میشود. لازم به ذکر است که این شی، Context Manager نیز به حساب میآید. بنابراین، متد ()__enter__. توسط عبارت With فراخوانی و مقدار بازگشتی آن به فایل مربوطه تخصیص داده میشود. پس از آن، امکان دستکاری فایل درون بلوک کد With وجود دارد. زمانی که بلوک خاتمه مییابد، متد ()__exit__. به صورت خودکار فراخوانی میشود و به دنبال آن، فایل بسته خواهد شد. در شرایطی که یک استثنا درون بلوک کد With رخ داده باشد نیز همچنان فایل به درستی بسته میشود.
با وجود اینکه استفاده از عبارت With نسبت به رویکرد Try – Finally کوتاهتر است، اما این روش تعمیمپذیری کمتری دارد. چرا که عبارت With تنها با آن دسته از اشیایی مورد استفاده قرار میگیرد که در آنها پروتکلهای مدیریت فضا پشتیبانی میشوند. در حالی که در روش Try – Finally، بدون نیاز به پشتیبانی از پروتکلهای مدیریت فضا، امکان اجرای اقدامات مربوط به پاکسازی هر شی دلخواهی وجود دارد. پس از پایتون ۳.۱ و نسخههای بعدی آن، عبارت With در پایتون از چندین Context Manager پشتیبانی میکند. به منظور پشتیبانی از چند Context Manager، باید آنها را با کاما یا همان ویرگول از یکدیگر جدا کرد. برای درک بهتر چگونگی جداسازی Context Managerهای مختلف از هم، باید به دستور زیر توجه شود:
with A() as a, B() as b:
pass
روش فوق مشابه یک With تودرتو عمل میکند. معمولاً این روش زمانی مفید خواهد بود که نیاز باشد دو فایل، یکی برای خواندن و دیگری برای نوشتن، به طور همزمان باز باشند. به منظور استفاده از دو فایل به طور همزمان با به کارگیری عبارت With، کدهای زیر لازم است:
with open("input.txt") as in_file, open("output.txt", "w") as out_file:
# Read content from input.txt
# Transform the content
# Write the transformed content to output.txt
pass
با کمک قطعه کد فوق میتوان دستورات مربوط به خواندن و انتقال محتوای فایل input.txt را اضافه کرد. پس از آن، میتوان درون همان بلوک کد، نتیجه نهایی را در فایل متنی output.txt نوشت. نکته قابل توجه این است که استفاده از چند Context Manager در یک عبارت With، کاستیهای مخصوص به خود را به همراه دارد. به عنوان مثال، در صورتی که از این ویژگی استفاده شود، به احتمال زیاد محدودیت طول خط کد شکسته میشود. برای رفع این موضوع، از Backslash به منظور ادامه دادن خطوط کد استفاده میشود.
چرا بهتر است از With در پایتون استفاده شود؟
دستور With در پایتون برای مدیریت منابع و استثناها کاربرد دارد. در بیشتر مواقع، دستور With هنگام کار با فایلهای جریانی استفاده میشود. به عنوان مثال، عبارت With این اطمینان را به وجود میآورد که در صورت وقوع استثناها، فایل فرآیند جریان، فرآیندهای دیگر را مسدود نکند و تنها خاتمه یابد.
برای روشنتر شدن کاربرد With در پایتون و مزیت استفاده از آن در بلوک کد زیر ابتدا رویکرد Try – Finally مربوط به مدیریت منابع جریان فایل نشان داده شده است:
file = open('file-path', 'w')
try:
file.write('Lorem ipsum')
finally:
file.close()
به طور معمول، روش فوق برای نوشتن روی یک فایل قابل استفاده است، اما بهتر است این کار با دستور With انجام شود. چرا که با کمک With در پایتون، کدنویسی بسیار خواناتر خواهد شد. عملیات نوشتن در فایل با به کارگیری عبارت With در قطعه کد زیر آمده است:
with open('file-path', 'w') as file: file.write('Lorem ipsum')
مزایای With در پایتون چیست؟
به کارگیری دستور With در پایتون مزیتهای مختلفی را برای برنامه نویسان به ارمغان میآورد. به عنوان مثال، به واسطه عبارت With در پایتون کدهایی که با منابع سیستم سروکار دارند به صورت خواناتر، با قابلیت استفاده مجدد، ایمنتر و مختصرتر ارائه میشوند. علاوه بر این، با به کارگیری عبارت With در پایتون اعمالی همچون جلوگیری از اشکالها و نشتها تسهیل مییابند. چرا که در این روش فراموش کردن مراحلی مانند پاکسازی، بستن فایل و آزادسازی منابع پس از اتمام کار همگی غیرممکن هستند.
در حقیقت، استفاده از With در پایتون این امکان را به وجود میآورد که بیشتر منطق مربوط به مدیریت منابع به صورت انتزاعی انجام شود. به طوری که هر بار به جای آن که از یک Try – Finally صریح به همراه مراحل راهاندازی و تخریب استفاده شود، با کمک عبارت With عملیات مربوطه انجام میشوند و در نتیجه، از تکرار آنها اجتناب خواهد شد. به منظور درک بهتر نحوه کار با عبارت With و کاربردهای آن، در ادامه آموزش With در پایتون ، به برخی از موارد استفاده عبارت With پرداخته میشود. برخی از مزیتهای مهم استفاده از عبارت With در پایتون به شرح زیر است:
- مدیریت منابع با عبارت With نسبت به روش Try – Finally، به طور ایمنتری انجام میشود.
- استانداردهای استفاده از رویکرد Try – Finally در Context Manager پوشش داده میشوند.
- با استفاده از With در پایتون امکان استفاده مجدد از کدهایی فراهم میشود که به طور خودکار مراحل راهاندازی و تخریب یک عملیات معین را مدیریت میکنند.
- عبارت With به اجتناب از نشت منبع کمک میکند.
کاربردهای With در پایتون
امروزه توسعه دهندگان از عبارت With در پایتون به میزان زیادی استفاده میکنند. استفاده مداوم از این عبارت نشان میدهد که این ابزار چندین مورد استفاده ارزشمند دارد. به عنوان مثال، عبارت With این اطمینان را به وجود میآورد که در صورت وقوع استثناها، فایل فرآیند جریان، فرآیندهای دیگر را بلوک نکند و تنها خاتمه یابد. در حال حاضر اشیا متعددی در کتابخانه استاندارد پایتون از پروتکل مدیریت فضا پشتیبانی میکنند که میتوان در یک دستور With این اشیا را به کار برد.
در این بخش از آموزش With در پایتون ، به نحوه استفاده از دستور With با چندین کلاس، هم در کتابخانه استاندارد و هم در کتابخانههای شخص ثالث پرداخته میشود. موارد استفاده و کاربردهای With در پایتون به شرح زیرند:
- کار با فایلها
- پیمایش دایرکتوریها (Traversing Directories)
- انجام محاسبات با دقت بالا
- مدیریت قفلها در برنامههای چندنخی (Multithreaded Programs)
- آزمایش موارد استثنایی با Pytest
با توجه به اهمیت بالا و کاربردهای مختلف عبارت With در پایتون، در ادامه هر یک از این موارد استفاده به طور کامل شرح داده میشوند.
کاربردهای With در پایتون : کار با فایلها
پیش از این از ()Open به منظور ارائه Context Manager و دستکاری فایلها با عبارت With استفاده شد. به طور کلی، به کارگیری عبارت With در پایتون ، یک روش پیشنهادی مرسوم برای باز کردن فایل به حساب میآید. چرا که در این رویکرد تضمین میشود که پس از خروج جریان اجرا از بلوک، توصیفکنندههای فایل مجدداً به طور خودکار با کد بسته میشوند.
همانطور که پیشتر به آن اشاره شد، رایجترین روش برای باز کردن فایل، استفاده از ()Open پیشساخته (Built-in) است:
with open("hello.txt", mode="w") as file:
file.write("Hello, World!")
در چنین شرایطی، به دلیل اینکه Context Manager پس از خروج از بلوک کد With، فایل را میبندد، یکی از اشتباههای رایج در این زمینه به صورت زیر است:
>>> file = open("hello.txt", mode="w")
>>> with file:
... file.write("Hello, World!")
...
13
>>> with file:
... file.write("Welcome to Real Python!")
...
Traceback (most recent call last):
File "", line 1, in
ValueError: I/O operation on closed file.
با اجرای قطعه کد فوق، عبارت With اول باعث میشود “!Hello, World” در فایل متنی hello.txt نوشته شود. باید توجه کرد که write(). تعداد بایتهای نوشته شده در فایل، یعنی ۱۳ را بازمیگرداند. اما زمانی که عبارت With دوم اجرا میشود، یک خطای ValueError به وجود میآید، چون فایل مربوطه قبلاً بسته شده است. یکی از راههای کاربردی دیگر برای باز کردن و مدیریت فایلها، استفاده از pathlib.Path.open() به حساب میآید که به صورت زیر است:
>>> import pathlib
>>> file_path = pathlib.Path("hello.txt")
>>> with file_path.open("w") as file:
... file.write("Hello, World!")
...
13
Path یک کلاس است که با کمک آن امکان نمایش مسیرهای مشخص به فایلهای فیزیکی موجود در رایانه وجود دارد. فراخوانی open(). روی یک شی Path که به یک فایل فیزیکی اشاره میکند، دقیقاً مانند open() فایل را باز میکند. در واقع، عملکرد Path.open() مشابه open() است، اما مسیر فایل به طور خودکار توسط شی Path مخصوصی ارائه میشود که متد روی آن فراخوانی خواهد شد.
با توجه به اینکه با کمک pathlib یک روش مطلوب و ساده برای دستکاری مسیرهای سیستم فایل ارائه میشود، بنابراین میتوان استفاده از عبارتهای With در پایتون را به عنوان یک ابزار مناسب و کاربردی در برنامه نویسی در نظر گرفت. در نهایت، هر زمان که یک فایل خارجی بارگذاری شود، لازم است مشکلات احتمالی گوناگونی مانند فایل از دسترفته، دسترسی به نوشتن و خواندن و سایر موارد به طور کامل مورد بررسی قرار بگیرند. در ادامه به الگوی کلی ضروری برای کار با فایلها اشاره شده است:
import pathlib
import logging
file_path = pathlib.Path("hello.txt")
try:
with file_path.open(mode="w") as file:
file.write("Hello, World!")
except OSError as error:
logging.error("Writing to file %s failed due to: %s", file_path, error)
در قطعه کد فوق، عبارت With در یک عبارت Try… Except قرار داده شده و در صورتی که در حین اجرای With خطای OSError رخ دهد، میتوان با یک پیام کاربرپسند و توصیفی خطای مربوطه را ثبت کرد.
کاربردهای With در پایتون : پیمایش دایرکتوریها
در ماژول os تابعی به نام scandir() ارائه میشود. این تابع یک تکرار کننده (پیمایشگر | Iterator) روی اشیا os.DirEntry مربوط به ورودیهای یک دایرکتوری معین را بازمیگرداند. علاوه بر این، تابع scandir() به طور ویژه برای ارائه عملکرد بهینه در هنگام عبور از یک ساختار دایرکتوری طراحی شده است. با فراخوانی scandir()، همراه با مسیر، یک دایرکتوری معین به عنوان آرگومان و یک تکرار کننده نیز بازگردانده میشود که در آن امکان پشتیبانی از پروتکل مدیریت فضا وجود دارد.
>>> import os
>>> with os.scandir(".") as entries:
... for entry in entries:
... print(entry.name, "->", entry.stat().st_size, "bytes")
...
Documents -> 4096 bytes
Videos -> 12288 bytes
Desktop -> 4096 bytes
DevSpace -> 4096 bytes
.profile -> 807 bytes
Templates -> 4096 bytes
Pictures -> 12288 bytes
Public -> 4096 bytes
Downloads -> 4096 bytes
در مثال فوق، یک عبارت With با os.scandir() به عنوان تامینکننده مدیریت فضا نوشته شده است. پس از آن، روی ورودیهای دایرکتوری انتخاب شده (“.”) تکرار (Iterate) اتفاق میافتد و نام و اندازه آنها روی صفحه چاپ میشوند. در چنین شرایطی، scandir.close() با کمک .__exit__(). فراخوانی میشود تا پیمایشگر را ببندد و منابع به دست آمده را آزاد کند. البته باید توجه کرد که اگر این مثال روی سیستم دیگری اجرا شود، بسته به محتوای دایرکتوری فعلی آن سیستم، خروجی متفاوتی دریافت خواهد شد.
کاربردهای With در پایتون : انجام محاسبات با دقت بالا
بر خلاف اعداد ممیز شناور پیشساخته، ماژول اعشاری برای تنظیم دقت (Precision) در محاسبات معینی کاربرد دارد که شامل اعداد اعشاری هستند. دقت به طور پیشفرض در مکان ۲۸ تنظیم شده است.
میتوان بسته به نیازمندیها، آن را برای اعمال مختلف تغییر داد. یک راه سریع برای انجام محاسبات با دقت سفارشی، استفاده از localcontext() از Decimal است:
>>> from decimal import Decimal, localcontext
>>> with localcontext() as ctx:
... ctx.prec = 42
... Decimal("1") / Decimal("42")
...
Decimal('0.0238095238095238095238095238095238095238095')
>>> Decimal("1") / Decimal("42")
Decimal('0.02380952380952380952380952381')
در قطعه کد فوق، با کمک localcontext() یک Context Manager ارائه میشود که به واسطه آن، یک فضای اعشاری محلی (Local Decimal Context) ایجاد خواهد شد. با استفاده از این فضای اعشاری محلی میتوان محاسبات را همراه با یک دقت سفارشیشده انجام داد. در بلوک کد With، باید prec. روی دقت جدید مورد نظر تنظیم شود. به عنوان مثال، در قطعه کد فوق، دقت سفارشی شده، 42 است. وقتی که بلوک کد With به پایان برسد، دقت به مقدار پیشفرض خود یعنی مکان 28 بازنشانی میشود.
کاربردهای With در پایتون : مدیریت قفلها در برنامههای چندنخی
یکی دیگر از کاربردهای موثر عبارت With در پایتون Threading.Lock است. این کلاس یک قفل اولیه ایجاد میکند. این قفل اولیه از تغییر همزمان یک منبع مشترک (توسط چندین رشته) در یک اپلیکیشن چندنخی جلوگیری میکند. میتوان یک شی Lock را به عنوان context Manager در یک عبارت With استفاده کرد تا بدین طریق، بدست آوردن و آزادسازی یک قفل خاص به طور خودکار انجام شود. به عنوان مثال، کدهای زیر برای حفاظت از موجودی حساب بانکی کاربرد دارند:
import threading
balance_lock = threading.Lock()
# Use the try ... finally pattern
balance_lock.acquire()
try:
# Update the account balance here ...
finally:
balance_lock.release()
# Use the with pattern
with balance_lock:
# Update the account balance here ...
در عبارت With فوق، زمانی که جریان وارد عبارت و سپس، از آن خارج میشود، گرفتن و آزادسازی قفل به صورت خودکار انجام میشود. به این ترتیب، این امکان برای برنامه نویسان ایجاد میشود که روی بخشهای پراهمیت کدنویسی تمرکز کنند و دیگر زمانی را برای انجام عملیات تکراری اختصاص ندهند. در مثال فوق، بخش Lock موجود در عبارت With با نام ناحیه بحرانی (Critical Section) شناخته میشود. در علم کامپیوتر، منظور از ناحیه بحرانی محیطی است که به صورت حفاظت شده برای اعمال مختلف مورد استفاده قرار میگیرد. در مثال فوق، ناحیه بحرانی از دسترسی «همزمان» به موجودی حساب جلوگیری میکند.
کاربردهای With در پایتون : آزمایش موارد استثنایی با Pytest
تا این بخش از آموزش With در پایتون به برخی از کاربردهای مختلف Context Manager موجود در کتابخانه استاندارد پایتون اشاره شد. اکنون در ادامه این بخش، چندین کتابخانه شخص ثالثی معرفی میشوند که اشیای این کتابخانهها پروتکل مدیریت فضا را پشتیبانی میکنند.
حال میتوان با استفاده از Pytest کدها را آزمایش کرد. چرا که معمولاً برخی از توابع و بلوکهای کد در شرایط خاص باعث ایجاد استثناها میشوند و چنین مواردی باید آزمایش شوند. تابع pytest.raises() برای این منظور مورد استفاده قرار میگیرد. با توجه به اینکه با به کارگیری pytest.raises() یک Context Manager ارائه میشود، میتوان آن را به صورت زیر درون عبارت With در پایتون استفاده کرد:
>>> import pytest
>>> 1 / 0
Traceback (most recent call last):
File "", line 1, in
ZeroDivisionError: division by zero
>>> with pytest.raises(ZeroDivisionError):
... 1 / 0
...
>>> favorites = {"fruit": "apple", "pet": "dog"}
>>> favorites["car"]
Traceback (most recent call last):
File "", line 1, in
KeyError: 'car'
>>> with pytest.raises(KeyError):
... favorites["car"]
...
در قسمت اول مثال فوق، با کمک pytest.raises()، خطای ZeroDivisionError قابل تشخیص است. در بخش دوم قطعه کد با کمک تابع خطای KeyError پیدا شد. خطای KeyError زمانی ایجاد میشود که کلید مورد دسترسی در یک دیکشنری وجود نداشته باشد. در صورتی که تابع یا بلوک کد استثناهای مورد انتظار را نشان ندهد، pytest.raises() به صورت زیر یک استثنا شکست نمایش میدهد:
>>> import pytest
>>> with pytest.raises(ZeroDivisionError):
... 4 / 2
...
2.0
Traceback (most recent call last):
...
Failed: DID NOT RAISE
یکی از کاربردهای جذاب دیگر pytest.raises() این است که میتوان با استفاده از آن یک متغیر هدف را برای بررسی استثناها مشخص کرد. به عنوان مثال، به منظور بررسی پیام خطا میتوان از کدهای زیر استفاده کرد:
>>> with pytest.raises(ZeroDivisionError) as exc:
... 1 / 0
...
>>> assert str(exc.value) == "division by zero"
برای پیدا کردن استثناهای توابع و بلوک کد، امکان استفاده از ویژگیهای (Featureهای) مختلف pytest.raises() وجود دارد. در حقیقت، pytest.raises() به عنوان یک ابزار کمکی و مفید شناخته میشود که برنامه نویسان میتوانند آن را در راهبردهای آزمایشی فعلی خود به کار ببرند. در ادامه این مقاله برخی از فیلمهای مربوط به زبان برنامه نویسی پایتون به طور خلاصه و مختصر معرفی شدهاند.
استفاده از عبارت Async With در پایتون چگونه است؟
دستور With یک نسخه ناهمگام (Asynchronous) نیز دارد، عبارت Async With برای ایجاد Context Managerهایی به کار میرود که به کد ناهمزمان یا همان ناهمگام وابسته هستند. مشاهده عبارت Async With در این نوع کدها بسیار رایج است، چرا که بسیاری از عملیات ورودی-خروجی (IO) شامل مراحل راهاندازی و تخریب میشوند.
برای مثال، ممکن است نیاز باشد که کدنویسی یک تابع ناهمگام برای بررسی آنلاین بودن یک سایت انجام شود. میتوان از عبارت With همراه با aiohttp ،asyncio و async with به صورت زیر استفاده کرد:
# site_checker_v0.py
import aiohttp
import asyncio
async def check(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
print(f"{url}: status -> {response.status}")
html = await response.text()
print(f"{url}: type -> {html[:17].strip()}")
async def main():
await asyncio.gather(
check("https://realpython.com"),
check("https://pycoders.com"),
)
asyncio.run(main())
توضیحات کدهای فوق به شرح زیر است.
- خط سوم: این خط از کد فوق برای Import کردن aiohttp به کار میرود. با به کارگیری aiohttp، یک سرور و کلاینت HTTP ناهمگام برای Asyncio و پایتون ارائه میشود. باید توجه کرد که aiohttp یک پکیج یا همان بسته شخص ثالث است و امکان نصب آن با اجرای کد python -m pip install aiohttp در خط فرمان وجود دارد.
- خط چهارم: در این خط از قطعه کد، با نوشتن imports Asyncio، امکان کدنویسی همگام با استفاده از سینتکس Await و Async وجود دارد.
- خط ششم: در این خط کد، ()check به عنوان یک تابع ناهمگام تعریف میشود. لازم به ذکر است که درون تابع ()check دو عبارت Async With تودرتو فراخوانی شده است.
- خط هفتم: با کمک این خط از کد فوق، یک Async خارجی تعریف میشود و با به کارگیری آن، یک نمونهگیری از ()aiohttp.ClientSession برای دریافت یک Context Manager حاصل خواهد شد. این شی برگشتی در session ذخیره میشود.
- خط هشتم: با استفاده از URL به عنوان آرگومان، یک عبارت Async With داخلی تعریف میشود که با استفاده از آن، get() فراخوانی خواهد شد. در این قسمت از قطعه کد Context Manager دوم ساخته و یک response برگردانده میشود.
- خط نهم: این خط به منظور چاپ کد وضعیت (Status Code) پاسخ برای URL مربوطه کاربرد دارد.
- خط یازدهم: به منظور چاپ URL سایت و نوع مستندات (Doctype) استفاده میشود.
- خط سیزدهم: در این خط از تکه کد فوق، اسکریپت مربوط به تابع ()main تعریف میشود.
- خط چهاردهم: به منظور فراخوانی gather() از Asyncio به کار میرود. این تابع «اشیا غیرقابل مقایسه» (Objects Awaitable) را در یک دنباله به طور همگام اجرا میکند. لازم به ذکر است که Awaitable Objects آن دسته از اشیایی هستند که در عبارت await مورد استفاده قرار میگیرند. در قطعه کد فوق gather() دو نمونه (Instance) از check() را همراه با URLهای مختلف اجرا میکند.
- خط نوزدهم: در نهایت، با استفاده از Asyncio.run() تابع ()main اجرا میشود. با کمک این تابع میتوان یک حلقه رویداد (Event Loop) برای Asyncio ایجاد کرد و آن را در پایان عملیات بست. منظور از Event Loop هسته اصلی هر اپلیکیشن Asyncio است. با به کارگیری حلقههای رویداد، اعمالی همچون اجرای وظایف و فراخوانیهای ناهمزمان، انجام عملیات IO شبکه و اجرای فرآیندهای فرعی انجام میشود.
اکنون در ادامه خروجی حاصل از اجرای قطعه کد فوق نمایش داده شده است:
$ python site_checker_v0.py
https://realpython.com: status -> 200
https://pycoders.com: status -> 200
https://pycoders.com: type ->
https://realpython.com: type ->
همانطور که در خروجی کد قابل مشاهده است، اسکریپت به درستی کار میکند و در حال حاضر، هر دو وب سایت در دسترس هستند. علاوه بر این، امکان بازیابی اطلاعات مربوط به نوع سند از طریق صفحه اصلی هر یک از این سایتها وجود دارد. البته باید توجه کرد که به دلیل ماهیت غیرقطعی زمانبندی وظایف همگام و تأخیر (Latency) شبکه، خروجی میتواند کمی متفاوت به نظر برسد. به طور ویژه، ممکن است ترتیب خطوط کد به صورت متفاوتی ظاهر شوند.
تفاوت عمده عبارت With و Async With چیست؟
به طور کلی، عملکرد عبارت With در پایتون با Async With شبیه به یکدیگر هستند، اما برای کار با Async With، به Context Manager ناهمگام نیاز است. به بیان ساده، Async With به یک Context Manager نیاز دارد تا بتوان اجرای متدهای خروجی و ورودی آن را تعلیق کرد.
در Context Managerهای ناهمگام امکان پیادهسازی متدهای خاصی مانند __aenter__(). و __aexit__(). وجود دارد. این متدهای خاص دقیقاً با متدهای ()__enter__. و __exit__(). یک Context Manager معمولی مطابقت دارند.
ایجاد Context Manager سفارشی
در بخشهای پیشین از آموزش With در پایتون ، نحوه کار با Context Manager از کتابخانه استاندارد و کتابخانههای شخص ثالث بررسی شدند. همانطور که مشخص است هیچ ترفند جادویی یا وایژهای در مورد کار با open()، decimal.localcontext() ،threading.Lock و سایر موارد وجود ندارد. در واقع، این توابع تنها اشیایی را برمیگردانند که پروتکل مدیریت فضا در آنها قابل پیادهسازی باشد. از سوی دیگر، با پیادهسازی متدهای خاص __enter__(). و __exit__(). در Context Managerهای مبتنی بر کلاس نیز میتوان به عملکرد مشابهی دسترسی داشت.
علاوه بر این، با کمک دکوراتور contextlib.contextmanager از کتابخانه استاندارد و یک تابع مولد کدگذاری شده مناسب، امکان ایجاد Context Managerهای مبتنی بر تابع وجود دارد. به طور کلی، کاربردهای عبارت With و Context Managerها تنها به مدیریت منابع محدود نمیشوند. بلکه با به کارگیری آنها امکان ارائه و استفاده مجدد کدهای راهاندازی و تخریب رایج نیز وجود دارد. به بیان ساده، با استفاده از Context Managerها، میتوان جفت عملیاتی را انجام داد که باید قبل و بعد از عملیات یا رویه دیگری انجام شوند. در ادامه به برخی از این عملیات اشاره میشود:
- باز کردن و بستن
- قفل و آزادسازی
- تغییر و بازنشانی
- ساخت و حذف
- ورود و خروج
- شروع و پایان
- راهاندازی و تخریب
برای اینکه جفت عملیات فوق به صورت ایمن مدیریت شوند، میتوان کدهای خاصی را به کار برد. امکان استفاده مجدد آن در عبارتهای With مختلف درون یک Context Manager وجود دارد. علاوه بر اینکه معمولاً این ویژگی از بروز خطاهای مختلف و کدهای تکراری جلوگیری میکند، باعث میشود APIها ایمنتر، خواناتر و کاربرپسندتر ارائه شوند.
ایجاد API مطلوب با استفاده از Context Manager در پایتون
Context Managerها کاملاً منعطف هستند و در صورتی که از دستور With در پایتون به شکل خلاقانه استفاده شود، میتوان با به کارگیری آن APIهای مناسبی برای کلاسها، ماژولها و بستهها تعریف کرد. به عنوان مثال، اگر منبعی که قصد مدیریت آن وجود دارد، سطح تورفتگی متنی در نوعی اپلیکیشن تولید گزارش باشد چه باید کرد؟
در چنین حالتی میتوان کدها را به صورت زیر نوشت:
with Indenter() as indent:
indent.print("hi!")
with indent:
indent.print("hello")
with indent:
indent.print("bonjour")
indent.print("hey")
باید توجه کرد که در قطعه کد فوق، این کد چندین بار وارد یک Context Manager شده و از آن خارج میشود. در نتیجه، بین سطوح مختلف تورفتگی جابجا خواهد شد. اجرای این قطعه کد، چاپ خروجی متنی با فرمت تقریباً منظم زیر را به دنبال دارد:
hi!
hello
bonjour
hey
اما چگونه باید یک Context Manager را پیادهسازی کرد که عملکرد فوق در آن پشتیبانی شود؟ با استفاده از کدهای زیر این پیادهسازی امکانپذیر است:
class Indenter:
def __init__(self):
self.level = -1
def __enter__(self):
self.level += 1
return self
def __exit__(self, exc_type, exc_value, exc_tb):
self.level -= 1
def print(self, text):
print(" " * self.level + text)
در قطعه کد فوق، هر بار که جریان اجرا وارد Context میشود، متد __enter__(). عمل افزایش level. را انجام میدهد. علاوه بر این، متد __enter__(). نمونه فعلی، self را برمیگرداند. درون __exit__(). باید level. کم شود تا هر خروج از Context، متن تایپشده یک سطح تورفتگی به عقب برود. نکته کلیدی از مثال فوق این است که مقدار self بازگشتی از __enter__(). امکان استفاده مجدد از یک Context Manager یکسان در چندین عبارت With تودرتو وجود دارد. به بیان ساده، با هر بار ورود و خروج از یک Context خاص، سطح تورفتگی متن تغییر میکند.
ساخت Context Manager ناهمگام
به منظور ساخت Context Manager ناهمگام، لازم است متدهای __aenter__(). و __aexit__(). تعریف شوند. اسکریپت زیر پیادهسازی مجدد فایل site_checker_v0.py است که پیشتر در بخشهای قبلی این مقاله به آن پرداخته شد.
با این تفاوت که در اسکریپت زیر یک Context Manager ناهمگام سفارشیسازی شده ایجاد میشود. در این Context Manager ناهمگام عملکردهای بستن و ساختن session انجام میشوند. کدهای مربوط به آن به صورت زیر است:
# site_checker_v1.py
import aiohttp
import asyncio
class AsyncSession:
def __init__(self, url):
self._url = url
async def __aenter__(self):
self.session = aiohttp.ClientSession()
response = await self.session.get(self._url)
return response
async def __aexit__(self, exc_type, exc_value, exc_tb):
await self.session.close()
async def check(url):
async with AsyncSession(url) as response:
print(f"{url}: status -> {response.status}")
html = await response.text()
print(f"{url}: type -> {html[:17].strip()}")
async def main():
await asyncio.gather(
check("https://realpython.com"),
check("https://pycoders.com"),
)
asyncio.run(main())
در اسکریپت فوق، منطق مربوط به عبارت Async With خارجی استخراج و در AsyncSession گنجانده میشود. خروجی قطعه کد فوق به صورت زیر است:
$ python site_checker_v1.py
https://realpython.com: status -> 200
https://pycoders.com: status -> 200
https://realpython.com: type ->
https://pycoders.com: type ->
به این ترتیب آموزش With در پایتون در این مقاله در اینجا به پایان میرسد. در بخش انتهایی به معرفی برخی از دورههای کاربردی آموزش پایتون تم آف پرداخته شده است.
فیلم های آموزش پایتون تم آف
در این بخش پایانی از آموزش With در پایتون ، برخی از دورههای آموزشی مربوط به زبان برنامه نویسی پایتون مجموعه تم آف معرفی شدهاند. ترتیب معرفی این دورهها به گونهای ارائه شده است که پس از اتمام هر دوره میتوان دوره بعدی را شروع کرد. ابتدا دوره مقدماتی پایتون تم آف معرفی شده است تا آشنایی با مباحث پایهای پایتون حاصل شود. پس از آن، یک دوره آموزش پایتون به همراه مثالهای عملی شرح داده میشود. علاوه بر این، با توجه به اهمیت مفاهیم شیگرایی در برنامه نویسی، استفاده از دوره آموزش شیگرایی در پایتون پیشنهاد میشود. همچنین در پایان، دورههای شاخص دیگری از جمله دوره پروژه محور برای یادگیری عملی پایتون معرفی میشوند.
فیلم آموزش برنامه نویسی پایتون (Python) – مقدماتی
این دوره آموزشی در سطح مقدماتی ارائه شده و به همین دلیل برای افرادی مناسب است که قصد شروع یادگیری زبان برنامه نویسی پایتون را دارند. طول مدت این دوره نزدیک به ۲۰ ساعت و مدرس آن مهندس پژمان اقبالی شمس آبادی است. در این دوره آموزشی، به مفاهیم مقدماتی زبان برنامهنویسی پایتون به زبان ساده و در عین حال به صورت جامع پرداخته و کلیه مباحث پایه پیرامون پایتون ارائه میشود. این دوره آموزشی پیشنیازی ندارد و برای علاقهمندان به برنامهنویسی در تمامی زمینهها مناسب است. از جمله سرفصلها و موضوعاتی که در این دوره ارائه شدهاند، میتوان به آموزش نصب پایتون، کتابخانه استاندارد پایتون، ساختمان داده در پایتون، توابع و ماژولها، کلاس در پایتون، خواندن و نوشتن فایلها و سایر مباحث مقدماتی پایتون اشاره کرد.
- برای مشاهده فیلم آموزش برنامه نویسی پایتون (Python) – مقدماتی + اینجا کلیک کنید.
فیلم آموزش زبان برنامه نویسی پایتون (Python) همراه با مثال های عملی
پس از گذراندن دوره مقدماتی، میتوان از دوره پایتون با مثالهای عملی نیز استفاده کرد. طول مدت این دوره آموزشی، بیش از ۱۳ ساعت و مدرس آن دکتر فرشید شیرافکن است. این دوره آموزشی نیز در سطح مقدماتی و بدون هیچ پیشنیازی تهیه شده است. در این دوره آموزش پایتون، پس از آموزش مباحث نظری، از مثالهای عملی با هدف درک بیشتر و یادگیری کاربردی پایتون استفاده میشود. از جمله مباحثی که در دوره آموزشی پایتون همراه با مثالهای عملی ارائه شده، میتوان به انواع دادهها در پایتون، نحوه نامگذاری صحیح متغیرها، عملگرها در پایتون، دستورات شرطی، حلقهها، رشته، لیست، تاپل (Tuple)، دیکشنری، توابع، فایلها، عبارتهای منظم، الگوریتمهای مرتبسازی و بسیاری دیگر از مباحث مهم و اساسی Python اشاره کرد.
- برای مشاهده فیلم آموزش زبان برنامه نویسی پایتون (Python) همراه با مثال های عملی + اینجا کلیک کنید.
فیلم آموزش برنامه نویسی شی گرا در Python (پایتون)
- برای مشاهده فیلم آموزش برنامه نویسی شی گرا در Python (پایتون) + اینجا کلیک کنید.
فیلم آموزش پروژه محور Python (پایتون) – ساخت نرم افزار برای Windows و Linux
در این دوره تم آف، ابتدا ابزار توسعه پای کیوت (PyQt) معرفی میشود و در ادامه مخاطبین با نحوه ساخت نرمافزار برای ویندوز و لینوکس آشنا خواهند شد. طول مدت این دوره آموزشی بیش از ۹ ساعت، شامل پنج فصل و مدرس آن مهندس محمد حسینی است. در این دوره آموزشی، سرفصلهایی همچون ساختار اصلی برنامه نویسی گرافیکی با ابزار PyQt، ابزار PyQt-Designer، برنامه نویسی ویجتهای گوناگون نرمافزار و استفاده از برنامه نویسی ماژولار و بسیاری از موارد دیگر مورد بررسی قرار میگیرند. این دوره آموزشی مناسب افرادی است که میخواهند زبان برنامه نویسی پایتون و نحوه ساخت نرمافزار را به صورت پروژه محور فرا بگیرند.
- برای مشاهده فیلم آموزش پروژه محور Python (پایتون) – ساخت نرم افزار برای Windows و Linux + اینجا کلیک کنید.
فیلم آموزش جنگو (Django)
دوره آموزشی جنگو مناسب افرادی است که به برنامه نویسی وب علاقهمند هستند. مدت زمان این دوره آموزشی بیش از پنج ساعت و مدرس آن، مهندس پدرام شاه صفی است. فیلم آموزش جنگو به صورت پروژه محور ارائه شده است. به همین دلیل، پس از تدریس مباحث نظری، آموزشهای عملی در قالب پروژههای نمونه شرح داده خواهند شد. به واسطه مثالهای عملی این دوره آموزشی، درک و یادگیری مفاهیم بهتر و سادهتر انجام میشود. از جمله سرفصلها و عناوین دوره آموزش جنگو میتوان به نصب و آمادهسازی جنگو، پایگاه داده و مدلها، URLها، صفحه ادمین، قالبها و سایر مباحث کاربردی و مهم در جنگو اشاره کرد.
- برای مشاهده فیلم آموزش جنگو (Django) – فریمورک تحت وب با پایتون (Python) + اینجا کلیک کنید.
جمعبندی
با توجه به اهمیت روزافزون برنامه نویسی به ویژه کدنویسی با پایتون در بازار کار کنونی، آموزش عبارتهای خاص این زبان برنامه نویسی، از جمله یادگیری نحوه استفاده از عبارت With در پایتون میتواند به عنوان یک مهارت کلیدی و کاربردی برای برنامه نویسان بسیار کاربرد داشته باشد.
بنابراین در این مقاله ابتدا به مفاهیم مربوط به عبارت With در پایتون پرداخته شد و در بخشهای بعدی، کاربردهای مختلف این عبارت به طور مفصل مورد بررسی قرار گرفتند. علاوه بر این، در این مقاله عبارت Async With و تفاوتهای عمده آن با With در پایتون شرح داده شد. امید است این مقاله مفید واقع شده باشد.