שיעורי מודול (3/4)
מנהל הקשר: עם ו-__enter__/__exit__
מנהל הקשר הוא אובייקט שבו אתה משתמש עם with. זה מבטיח את זה
פעולות הגדרה/ניקוי מסוימות מתרחשות תמיד, גם כאשר מתרחשות שגיאות.
הדוגמה הקנונית: פתיחה וסגירה של קובץ.
תבנית with
with open("note.txt", "w") as f:
f.write("ciao")
# qui f è già chiuso, anche se write() ha sollevato un'eccezioneבאנגלית פשוטה: "עם משאב X פתוח כ-f, עשה זאת. בסיום
(מה שלא יקרה), סגור את X." בלי with היית צריך לכתוב
try/finally בכל פעם.
יותר מאחד בכל פעם
with open("a.txt") as f, open("b.txt") as g:
dati_a = f.read()
dati_b = g.read()כתיבת מנהל ההקשרים שלך (כיתה)
עליך להגדיר שתי שיטות דנדר: __enter__ ו-__exit__.
class Timer:
def __enter__(self):
import time
self.t0 = time.perf_counter()
return self # è l'oggetto che finisce dopo "as"
def __exit__(self, exc_type, exc_value, traceback):
import time
self.elapsed = time.perf_counter() - self.t0
# ritornare True ingoia l'eccezione; di solito non lo vuoi
return False
with Timer() as t:
sum(range(10**6))
t.elapsed # quanto è durato__enter__(self)נקרא בכניסה לבלוק; הערך המוחזר הוא מה שאתה מקבל לאחרas.__exit__(self, exc_type, exc_value, tb)נקרא ביציאה (תמיד). אם הוא מחזיר אתTrue, כל חריגים נדחקים.
קיצור הדרך: @contextmanager
contextlib.contextmanager מאפשר לך לכתוב מנהל הקשר בתור
פונקציית גנרטור:
from contextlib import contextmanager
@contextmanager
def timer():
import time
t0 = time.perf_counter()
try:
yield # qui sta il blocco "with"
finally:
elapsed = time.perf_counter() - t0
print(f"durata: {elapsed:.4f}s")
with timer():
sum(range(10**6))הקוד לפני yield הוא __enter__, הקוד אחרי הוא __exit__.
ה-try/finally מבטיח ניקוי גם כאשר מתרחשות שגיאות.
להדחיק: התעלם חריג
from contextlib import suppress
with suppress(FileNotFoundError):
open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propagaמלכודת: with רק עבור אובייקטים שתומכים בכך
x = 42
with x: # AttributeError: __enter__
...כתיבת מנהלי הקשר עם @contextmanager
המודול contextlib של הספרייה הרגילה מייצא מעצב נוח ביותר בשם @contextmanager ליצירת מנהלי הקשר באמצעות פונקציות מחולל, במקום לכתוב מחלקות מפורטות המיישמות שיטות __enter__ ו__exit__. אתה פשוט כותב פונקציית מחולל שמשתמש ב-yield בודד:
from contextlib import contextmanager
@contextmanager
def manage_resource():
# __enter__ code
yield resource
# __exit__ codeנסה זאת בעצמך
השתמש ב-contextlib.suppress כדי לנסות את int('non-numero') תוך התעלמות מ- ValueError. לאחר הבלוק הקצה 'אוקי = נכון'. הערך 'בסדר'.
הצג רמז
suppress(ValueError) בולע רק ValueError.
הפתרון זמין לאחר 3 ניסיונות
סקירת התרגיל
כתוב 'איסוף' מנהל הקשר עם @contextmanager שמניב רשימה ריקה וביציאה, ממיר אותה ל-tuple (הדפיס אותה). השתמש בו כדי להוסיף 1, 2, 3 לרשימה בתוך הבלוק. בסוף, הערך את `len(collection)` כאשר `collection` היא הרשימה שהתקבלה.
הצג רמז
תניב פריטים כדי לחשוף את הרשימה בתוך בלוק עם.
הפתרון זמין לאחר 3 ניסיונות
אתגר נוסף
Write a custom context manager using the `@contextmanager` decorator from `contextlib`. The context manager must be named `mock_session`. Upon entry, it must append `'start'` to a global list `log_list = []`, yield control using `yield`, and upon exit append `'stop'`. Finally, evaluate `log_list` after using the context manager with `with mock_session():`.
הצג רמז
השתמש בתשואה בתוך mock_session כדי להשעות את הביצוע לפני הוספת 'עצור'.
הפתרון זמין לאחר 3 ניסיונות