آموزش with در پایتون
دستور With در پایتون از مهمترین دستورات مدیریت منابع محسوب میشود. در سالهای اخیر پایتون به یکی از خفنترین زبانهای برنامهنویسی دنیا تبدیل شد؛ این زبان برنامه نویسی دارای عبارات (Statements) مرموز و نکته داری هست که برنامه نویسی را برای ما راحت تر میکند که یکی از این عبارات with در پایتون است، که در دوره آموزش رایگان پایتون کامل در مورد آن صحبت شده است.
احتمالا از افرادی هستید که برای شما این سوال پیش آمده که with در پایتون چیست؟ و چه موقعهایی از آن استفاده میکنیم؟ در جواب باید بگم که with در پایتون زمانی استفاده میشود که بخواهیم با استفاده از الگوی استاندارد، به مدیریت منابع به صورت بهینه بپردازیم.
در ادامه مقاله این بحث برای شما شفافتر خواهد شد.
چرا از with استفاده کنیم؟
دستور With در پایتون به برنامه نویسها کمک میکند تا برخی الگوهای رایج مدیریت منابع (Resource Management) را به وسیله انتزاعی کردن کارکرد آنها و فراهم کردن امکان کنار گذاشتن و استفاده مجدد از آنها را پیادهسازی کنند.
برای درک بهتر کارکرد with در پایتون از یک تابع داخلی پایتون با نام open استفاده میکنیم.
فرض کنید فایلی داریم به نام hello.txt حال کد زیر رو مشاهده کنید:
with open ("hello.txt", "w") as file: file.write("Hello, world!")
همانطور که مشاهده میکنید به کمک دستور with در پایتون تابع open را استفاده کردیم تا فایل hello.txt در حالت نوشتن باز کنیم و درون آن متنی بنویسیم؛ اما سوال اصلی اینجاست کاربرد with دقیقا در این کد چیست و چرا از آن استفاده کردیم؟
برای کار با فایل توصیه میشود فایلها را با استفاده از عبارت with در پایتون؛ چون این کار باعث میشود در انتهای برنامه، ارتباط برنامه با فایل قطع شود و فایل بسته شود.
خب حالا فرض کنیم از دستور with در پایتون استفاده نکنیم؛ کد آن به چه صورت خواهد بود؟
در واقع عبارت with باعث میشود کد ما در هسته پایتون به شکل زیر نمایش داده و اجرا شود:
file = open("hello.txt", "w") try: file.write("hello, world") finally: file.close()
حالا تفاوت دو قطعه کدی که با with و بدون 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 در پایتون
زمانی که عبارت with در پایتون اجرا میشه، اعمال زیر رخ میدن:
- فراخوانی Expression به منظور در اختیار گرفتن Context Manager
- ذخیرهسازی متدهای ()__enter__ و ()__exit__ مربوط به Context Manager برای استفاده در آینده
- فراخوانی ()__enter__ در Context Manager و در صورت لزوم، نگاشت مقدار بازگشتی در target_var
- اجرای بلوک کد مربوط به with
- فراخوانی ()__exit__ در Context Manager پس از اتمام اجرای بلوک کد with
کاربردهای 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!")
ماژول Pathlib
یکی دیگر از راههای کاربردی برای باز کردن و مدیریت فایلها، استفاده از ماژول Pathlib به حساب میآید که به صورت زیر است:
>>> import pathlib >>> file_path = pathlib.Path("hello.txt") >>> with file_path.open("w") as file: ... file.write("Hello, World!") ...
با کمک ماژول Pathlib امکان نمایش مسیرهای مشخص به فایلهای فیزیکی موجود در رایانه وجود دارد. فراخوانی تابع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
انجام محاسبات با دقت بالا
بر خلاف اعداد ممیز شناور پیشساخته، ماژول اعشاری برای تنظیم دقت (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')
برنامههای چند نخی
یکی دیگر از کاربردهای موثر عبارت 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 ...
آزمایش موارد استثنایی با Pytest
تا این بخش از آموزش with در پایتون به برخی از کاربردهای مختلف Context Manager موجود در کتابخانه استاندارد پایتون اشاره شد. حالا در ادامه این بخش، چند کتابخانه شخص ثالثی معرفی میشوند که اشیای این کتابخانهها پروتکل مدیریت فضا رو پشتیبانی میکنند. حال میتوان با استفاده از Pytest کدها رو آزمایش کرد. چرا که معمولاً برخی از توابع و بلوکهای کد در شرایط خاص باعث ایجاد استثناها میشوند و چنین مواردی باید آزمایش شوند. تابع pytest.raises() برای این منظور مورد استفاده قرار میگیرد. با توجه به اینکه با به کارگیری pytest.raises() یک Context Manager ارائه میشود، میتوان آن را به صورت زیر درون دستور with در پایتون استفاده کرد:
>>> import pytest >>> 1 / 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero >>> with pytest.raises(ZeroDivisionError): ... 1 / 0 ... >>> favorites = {"fruit": "apple", "pet": "dog"} >>> favorites["car"] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'car' >>> with pytest.raises(KeyError): ... favorites["car"] ...
ایجاد 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ها ایمنتر، خواناتر و کاربرپسندتر ارائه بشن.
جمع بندی
با توجه به اهمیت روزافزون برنامه نویسی به ویژه کدنویسی با پایتون در بازار کار کنونی، آموزش عبارتهای خاص این زبان برنامه نویسی، از جمله یادگیری نحوه استفاده از دستور with در پایتون میتونه به عنوان یک مهارت کلیدی و کاربردی برای برنامه نویسا بسیار کاربرد داشته باشد. بنابراین در این مقاله ابتدا به مفاهیم مربوط به عبارت with در پایتون پرداخته شد و در بخشهای بعدی، کاربردهای مختلف این عبارت به طور اجمالی مورد بررسی قرار گرفت.
چگونه در پایتون حرفهایتر شویم؟
با خواندن مقالات مختلف احتمالا بتوانید تا حدی به بخشهای مختلف پایتون مسلط شوید اما قطعا بیبرنامه بودن و اصولی نبودن مسیر یادگیری باعث میشود شما نسبت به زبان برنامه نویسی پایتون دلسرد شوید یا اینکه آن را بصورت کامل یاد نگیرید؛ اما نگران این موضوع نباشید، تیم سبزلرن برای شما آموزش صفر تا صد پایتون را بصورت کاملا رایگان قرار داده تا بتوانید بهصورت اصولی و حرفهای در زبان پایتون متخصص شوید. اما این همه ماجرا نیست!! این دوره علاوه بر رایگان بودن آن، دارای پشتیبانی انلاین نیز هست و هرکجا به هر مشکلی برخوردید، افراد متخصص در این زبان آماده پاسخگویی به اشکالات و سوالات شما هستند، چی بهتر از این؟ پس فرصت را غنیمت بشمرید و همین الان در این دوره شرکت کنید و بصورت تخصصی یادگیری خودتان را شروع کنید.
عالی