عبارت with در پایتون به برنامه نویسا کمک میکنه تا برخی از الگوهای رایج مدیریت منابع (Resource Management) رو به وسیله انتزاعی کردن کارکردشون و فراهم کردن امکان کنار گذاشتن و استفاده مجدد ازشون پیادهسازی کنن.
برای توضیح کارکرد with در پایتون بیاید بریم سراغ یکی از محبوبترین کتابخونههای درونی پایتون به نام open. در کد زیر، فایل hello.txt توسط عبارت with در پایتون با حالت w که برای write هستش باز شده تا بتونیم مقادیری رو درون فایل بنویسیم.
with open (“hello.txt”, “w”) as file:
file.write(“Hello, world!”)
توصیه میشه فایلها رو با استفاده از عبارت with در پایتون باز کنیم چرا که اینکار باعث میشه در انتهای برنامه ارتباط برنامه به طور خودکار با فایل قطع بشه و فایل بسته بشه. حالا اگه از with استفاده نکنیم چطوری باید کدشو بنویسیم؟
در واقع عبارت with باعث میشه کد ما در هسته پایتون به شکل زیر در بیاد و اجرا بشه:
file = open(“hello.txt”, “w”)
try:
file.write(“hello, world”)
finally:
file.close()
حالا تفاوت دو قطعه کدی که با with و بدون with نوشتیم چیه؟ در قطعه کدی که بدون with نوشتیم تضمین نمیشه که اگه هنگام اجراش خطایی بروز کنه ارتباط برنامه با فایل به طور خودکار قطع بشه، اما در قطعه کدی که با with نوشتیم تضمین میشه که ارتباط برنامه به طور خودکار با فایل چه در صورت اجرا چه در صورت بروز خطا قطع بشه. در واقع با استفاده از عبارت 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 حاصل میشه. در حقیقت، این عبارت باید شی خاصی رو برگردونه که درش پروتکل مدیریت فضا پیادهسازی میشه. این پروتکل دارای دو متد مخصوص هستش:
• ()__enter__: این متد برای ورود به فضای زمان اجرا در عبارت with فراخوانی میشه.
• ()__exit__: این متد زمانی فراخوانی میشه که اجرای بلوک کد with به اتمام میرسه.
لازم به ذکر است که Specifier (شناساگر) as انتخابی هستش. در صورتی که target_var همراه با as به کار بره، مقدار بازگشتی از فراخوانی __enter__() در شی Context Manager به آن متغیر محدود میشه.
زمانی که عبارت with در پایتون اجرا میشه، اعمال زیر رخ میدن:
1. فراخوانی Expression به منظور در اختیار گرفتن Context Manager
2. ذخیرهسازی متدهای __enter__() و __exit__() مربوط به Context Manager برای استفاده در آینده
3. فراخوانی __enter__() در Context Manager و در صورت لزوم، نگاشت مقدار بازگشتی در target_var
4. اجرای بلوک کد مربوط به with
5. فراخوانی __exit__() در Context Manager پس از اتمام اجرای بلوک کد with
امروزه توسعه دهندگان از عبارت with در پایتون به میزان زیادی استفاده میکنن. استفاده مداوم از این عبارت نشون میده که این ابزار چندین مورد استفاده ارزشمند داره. به عنوان مثال، عبارت with این اطمینان رو به وجود میآره که در صورت وقوع استثناها، فایل فرآیند جریان، فرآیندهای دیگر را بلوک نکنه و تنها خاتمه پیدا کنه. در حال حاضر اشیا متعددی در کتابخانه استاندارد پایتون از پروتکل مدیریت فضا پشتیبانی میکنن که میتوان در یک دستور with این اشیا را به کار برد. در این بخش از آموزش with در پایتون، به نحوه استفاده از دستور with با چندین کلاس، هم در کتابخانه استاندارد و هم در کتابخانههای شخص ثالث پرداخته میشه. موارد استفاده و کاربردهای with در پایتون به شرح زیرن:
پیمایش دایرکتوریها (Traversing Directories)
انجام محاسبات با دقت بالا
مدیریت قفلها در برنامههای چندنخی (Multithreaded Programs)
آزمایش موارد استثنایی با Pytest
با توجه به اهمیت بالا و کاربردهای مختلف عبارت with در پایتون، در ادامه هر یک از این موارد استفاده شرح داده میشن:
پیش از این از open() به منظور ارائه Context Manager و دستکاری فایلها با عبارت with استفاده شد. به طور کلی، به کارگیری عبارت with در پایتون، یک روش پیشنهادی مرسوم برای باز کردن فایل به حساب میآد. چرا که در این رویکرد تضمین میشه که پس از خروج جریان اجرا از بلوک، توصیفکنندههای فایل مجدداً به طور خودکار با کد بسته بشن. همونطور که پیشتر بهش اشاره شد، رایجترین روش برای باز کردن فایل، استفاده از open()پیشساخته (Built-in) هستش:
with open("hello.txt", mode="w") as file:
file.write("Hello, World!")
path:
یکی از راههای کاربردی دیگه برای باز کردن و مدیریت فایلها، استفاده از pathlib.Path.open() به حساب میآد که به صورت زیر هستش:
>>> import pathlib
>>> file_path = pathlib.Path("hello.txt")
>>> with file_path.open("w") as file:
... file.write("Hello, World!")
...
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)
در ماژول 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
ادامه مطلب در: