ترفند پایتونی 3 – Context Managers
حتما پیش اومده در پایتون فایلی رو باز کنید و احتمالا اینکار رو با with انجام دادید. with در تعریف سادش، عبارتیه برای باز و بسته کردن آبجکت ها که به ما با خلاصه کردن عملکرد یک سری الگوهای مدیریت منابع کمک میکنه. به طور مثال برای باز و بسته کردن فایل ها می توان از این عبارت استفاده کرد و این اطمینان رو داشت که فایل های باز شده با این روش به صورت اتوماتیک بعد از اجرا کد بسته شوند. شکل کلی context manager به صورت زیر خواهد بود:
1 2 |
with EXPRESSION as VARIABLE: BLOCK |
و به عنوان نمونه ای پر کاربرد می توان به باز و بسته کردن فایل ها اشاره کرد:
1 2 |
with open('hello.txt', 'w') as f: f.write('hello, world!') |
در حقیقت کد بالا به صورت زیر در پایتون اجرا خواهد شد:
1 2 3 4 5 |
f = open('hello.txt', 'w') try: f.write('hello, world') finally: f.close() |
1 2 3 4 |
some_lock = threading.Lock() with some_lock: # Do something... |
نحوه نوشتن Context Manager (class-based)
خود ما هم میتونیم این عملکرد رو در توابع و کلاس ها با استفاده از with داشته باشیم که به context manager معروفه!. context manager یک رویه هوشمندانه است که آبجکت ساخته شده توسط شما، دنبال می کنه. تمام کاری که لازمه برای ساخت یک context manager انجام بشه اینه که از متودهای از پیش ساخته شده (built-in method)، __enter__ و __exit__ استفاده کنیم. به طور مثال در کد زیر با شروع with متود enter اجرا میشه و عبارت Salam, Rahman چاپ خواهد شد و بعد از انجام کدهای بلاک داخل with که در مثال زیر پرینت عبارت Good Work هستش، پروسه کد ما با عبارت موجود در متود exit بسته خواهد شد.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Greeter: def __init__(self, name): self.name = name def __enter__(self): print(f"Salam, {self.name}") return self def __exit__(self, exc_type, exc_value, exc_tb): print(f"Bye Bye, {self.name}") with Greeter('Rahman'): print('Good Work...!') |
مثال دیگه همون مثال بازکردن یک فایله که در کد زیر پس از باز کردن فایل مورد نظر در حالت write، مقدار مورد نظر رو در فایل چاپ می کنیم و فایل هم در انتها بسته خواهد شد.
1 2 3 4 5 6 7 8 9 |
class ManagedFile: def __init__(self, name): self.name = name def __enter__(self): self.file = open(self.name, 'w') return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() |
که برای استفاده به صورت زیر عمل می کنیم:
1 2 3 |
with ManagedFile('hello.txt') as f: f.write('hello, world!') f.write('bye now') |
روش دیگر (generator-based)
البته برای استفاده و نوشتن context manager روش بالا تنها روش نیست و میشه از روش generator-based با استفاده از ماژول contextlib و decorator هم این عملکرد رو پیاده کرد.
1 2 3 4 5 6 7 8 9 |
from contextlib import contextmanager @contextmanager def managed_file(name): try: f = open(name, 'w') yield f finally: f.close() |
مثال Indent
مثال دیگر از کاربرد context manager: کدی رو با context manager بنویسیم که خروجی، همانند شکل زیر داشته باشیم.
1 2 3 4 |
hi! hello bonjour hey |
1 2 3 4 |
++++ hi! ++++++++ hello ++++++++++++ bonjour ++++ hey |
که با نوشتن کد زیر نتیجه بالا حاصل شود.
1 2 3 4 5 6 7 |
with Indenter() as indent: indent.print('hi!') with indent: indent.print('hello') with indent: indent.print('bonjour') indent.print('hey') |
و اما کدی که این خروجی رو خواهد داشت:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Indenter: def __init__(self): self.level = 0 def __enter__(self): self.level+=1 return self def __exit__(self, exc_type, exc_val, exc_tb): self.level-=1 def print(self, text): print('\t'*self.level, text) |
مثال Code Block Timer
در این مثال مدت زمان اجرا کدی که در بلاک with قرار گرفته رو بدست خواهیم آورد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
from dataclasses import dataclass, field import time from typing import Callable, ClassVar, Dict, Optional class TimerError(Exception): """A custom exception used to report errors in use of Timer class""" @dataclass class Timer: timers: ClassVar[Dict[str, float]] = dict() name: Optional[str] = None text: str = "Elapsed time: {:0.4f} seconds" logger: Optional[Callable[[str], None]] = print _start_time: Optional[float] = field(default=None, init=False, repr=False) def __post_init__(self) -> None: """Add timer to dict of timers after initialization""" if self.name is not None: self.timers.setdefault(self.name, 0) def start(self) -> None: """Start a new timer""" if self._start_time is not None: raise TimerError(f"Timer is running. Use .stop() to stop it") self._start_time = time.perf_counter() def stop(self) -> float: """Stop the timer, and report the elapsed time""" if self._start_time is None: raise TimerError(f"Timer is not running. Use .start() to start it") # Calculate elapsed time elapsed_time = time.perf_counter() - self._start_time self._start_time = None # Report elapsed time if self.logger: self.logger(self.text.format(elapsed_time)) if self.name: self.timers[self.name] += elapsed_time return elapsed_time def __enter__(self): """Start a new timer as a context manager""" self.start() return self def __exit__(self, *exc_info): """Stop the context manager timer""" self.stop() with Timer(): time.sleep(2) |
مطالب جدید
دستهها
- Books (۱۲)
- Excel (۲)
- اکسل به زبان مثال …! (۹)
- ترفند های پایتونی (۶)
- هوش تجاری (۴۸)
- Power BI (۳۶)
- DAX (۱۳)
- Power Query (۹)
- SQL (۸)
- SSIS (۲)
- Power BI (۳۶)
- یادگیری ماشین (۸)
- ML Algorithm (۲)
- kNN (۲)
- pandas (۵)
- ML Algorithm (۲)
بایگانی
آمار بازدید
- ۰
- ۴
- ۵۲
- ۴۳,۶۲۵
- ۲۷ اردیبهشت, ۱۴۰۳