আগের File Handling lesson কেন তা ব্যাখ্যা না করেই with open('file.txt') as f: ব্যবহার করেছিল। একটা context manager নিশ্চিত করে একটা cleanup step চলে — একটা file বন্ধ করা, একটা lock ছেড়ে দেওয়া, একটা database connection বন্ধ করা — মাঝখানে একটা error ঘটলেও।
with open('notes.txt') as f:
content = f.read()
# এখানে f.close() স্বয়ংক্রিয়ভাবে চলে — উপরের কোড error দিলেওwith ছাড়া, একই safety-র জন্য একটা manual try/finally দরকার — একটা context manager-এর পুরো উদ্দেশ্য প্রতিবার এটা হাতে লেখার দরকার না হওয়া।
# তুলনার জন্য, manual সমতুল্য
f = open('notes.txt')
try:
content = f.read()
finally:
f.close()এই দুটো method implement করা যেকোনো ক্লাস with-এর সাথে ব্যবহার করা যায়।
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_value, traceback):
import time
print(f'Elapsed: {time.time() - self.start:.2f}s')
with Timer():
slow_operation()
# block শেষ হলে elapsed time স্বয়ংক্রিয়ভাবে print হয়একটা সহজ case-এর জন্য, @contextmanager দিয়ে decorate করা একটা generator function একটা পুরো ক্লাস লেখা এড়ায়।
from contextlib import contextmanager
import time
@contextmanager
def timer():
start = time.time()
yield
print(f'Elapsed: {time.time() - start:.2f}s')
with timer():
slow_operation()yield-এর আগে সবকিছু setup হিসেবে চলে (__enter__-এর মতো); পরে সবকিছু cleanup হিসেবে চলে (__exit__-এর মতো), আর block raise করলেও সবসময় চলে।
| Common context manager | যা clean up করে |
|---|---|
| open() | File বন্ধ করে |
| threading.Lock() | Lock ছেড়ে দেয় |
| একটা database connection object | Connection বন্ধ করে |
| unittest.mock.patch() | আসল, un-mocked মান ফিরিয়ে আনে |
সেই তিনটা __exit__ argument কীসের জন্য
Exception detail (exc_type, exc_value, traceback) পাওয়া __exit__ একটা context manager-কে শুধু একটা সফল block-এর পরে clean up না, একটা error-এ react করতে — এমনকি suppress করতেও দেয়। বেশিরভাগ সহজ context manager এগুলো ignore করে আর যাই হোক cleanup করে।