דילוג לתוכן המרכזי
מודול 9 · שיעור 3 מתוך 435/36 בקורס~12 min
שיעורי מודול (3/4)

מנהל הקשר: עם ו-__enter__/__exit__

מנהל הקשר הוא אובייקט שבו אתה משתמש עם with. זה מבטיח את זה פעולות הגדרה/ניקוי מסוימות מתרחשות תמיד, גם כאשר מתרחשות שגיאות. הדוגמה הקנונית: פתיחה וסגירה של קובץ.

תבנית with

Python
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 בכל פעם.

יותר מאחד בכל פעם

Python
with open("a.txt") as f, open("b.txt") as g:
    dati_a = f.read()
    dati_b = g.read()

כתיבת מנהל ההקשרים שלך (כיתה)

עליך להגדיר שתי שיטות דנדר: __enter__ ו-__exit__.

Python
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 מאפשר לך לכתוב מנהל הקשר בתור פונקציית גנרטור:

Python
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 מבטיח ניקוי גם כאשר מתרחשות שגיאות.

להדחיק: התעלם חריג

Python
from contextlib import suppress

with suppress(FileNotFoundError):
    open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propaga

מלכודת: with רק עבור אובייקטים שתומכים בכך

Python
x = 42
with x:  # AttributeError: __enter__
    ...

כתיבת מנהלי הקשר עם @contextmanager

המודול contextlib של הספרייה הרגילה מייצא מעצב נוח ביותר בשם @contextmanager ליצירת מנהלי הקשר באמצעות פונקציות מחולל, במקום לכתוב מחלקות מפורטות המיישמות שיטות __enter__ ו__exit__. אתה פשוט כותב פונקציית מחולל שמשתמש ב-yield בודד:

Python
from contextlib import contextmanager

@contextmanager
def manage_resource():
    # __enter__ code
    yield resource
    # __exit__ code

נסה זאת בעצמך

פעילות גופנית#python.m9.l3.e1
ניסיונות: 0טוען...

השתמש ב-contextlib.suppress כדי לנסות את int('non-numero') תוך התעלמות מ- ValueError. לאחר הבלוק הקצה 'אוקי = נכון'. הערך 'בסדר'.

טוען עורך...
הצג רמז

suppress(ValueError) בולע רק ValueError.

הפתרון זמין לאחר 3 ניסיונות

סקירת התרגיל

פעילות גופנית#python.m9.l3.e2
ניסיונות: 0טוען...

כתוב 'איסוף' מנהל הקשר עם @contextmanager שמניב רשימה ריקה וביציאה, ממיר אותה ל-tuple (הדפיס אותה). השתמש בו כדי להוסיף 1, 2, 3 לרשימה בתוך הבלוק. בסוף, הערך את `len(collection)` כאשר `collection` היא הרשימה שהתקבלה.

טוען עורך...
הצג רמז

תניב פריטים כדי לחשוף את הרשימה בתוך בלוק עם.

הפתרון זמין לאחר 3 ניסיונות

אתגר נוסף

פעילות גופנית#python.m9.l3.e3
ניסיונות: 0טוען...

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 ניסיונות