Skip to content
> 💻 🧠 קוד 1001 > > 🐍 Python > 🧲 איך "לתפוס" את print() בפייתון: תפיסת stdout בלי ספריות חיצוניות

🧲 איך "לתפוס" את print() בפייתון: תפיסת stdout בלי ספריות חיצוניות

בפייתון ניתן ללכוד את מה שמודפס למסך דרך print().
זה שימושי עבור בדיקות, דיבאג, לוגינג, או כדי לדכא פלט “רועש”.
נראה איך לעשות זאת באופן אלגנטי — תוך שימוש רק בספריית הסטנדרט וכמה שורות קוד בלבד.


🧩 הבנת Context Managers בפייתון

מהו Context Manager?

Context manager מנהל משאבים בצורה בטוחה ומסודרת.
הוא מגדיר התנהגות של אתחול וניקוי סביב בלוק קוד.

דוגמה: ניהול קבצים

with open("file.txt", "w") as f:
    f.write("שלום, עולם!")

✅ ניקוי אוטומטי: הקובץ ייסגר גם אם תתרחש חריגה.


למה Context Managers שימושיים

  • ניקוי אוטומטי – משאבים משתחררים לבד
  • בטיחות במקרה של שגיאות – הניקוי מתבצע גם אם מתרחשת חריגה
  • קוד קריא יותר – לוגיקת setup/teardown במקום אחד
  • ניתן לשימוש חוזר – מתאים לקבצים, locks, חיבורים לרשת או stdout

שימוש ב-@contextmanager מ-contextlib

הדקורטור @contextmanager מאפשר ליצור context managers בעזרת גנרטורים:

from contextlib import contextmanager

@contextmanager
def my_context():
    print("נכנסים להקשר")
    resource = "משאב כלשהו"
    try:
        yield resource
    finally:
        print("יוצאים מהקשר")

with my_context() as res:
    print("בתוך הקשר:", res)

פלט:

נכנסים להקשר
בתוך הקשר: משאב כלשהו
יוצאים מהקשר

💡 הסבר:

  • הקוד לפני yield → מתבצע בעת הכניסה
  • הערך המוחזר מ-yield → זמין כ-as res
  • הקוד אחרי yield → מתבצע ביציאה, גם אם התרחש חריגה

זה מושלם עבור שינוי זמני של מצב גלובלי, כמו תפיסת stdout.


⚡ סיכום: Context Managers עבור stdout/stderr

משימה / תרחישContext Manager מומלץ
תפוס רק את printcapture_stdout
תפוס רק שגיאות/חריגותcapture_stderr
תפוס stdout ו-stderr בו זמניתcapture_output
תפוס והצג בו זמניתcapture_stdout_tee / capture_output_tee

📦 תפיסת stdout

from contextlib import contextmanager
import sys, io

@contextmanager
def capture_stdout():
    old_stdout = sys.stdout
    sys.stdout = buffer = io.StringIO()
    try:
        yield buffer
    finally:
        sys.stdout = old_stdout

דוגמה:

with capture_stdout() as out:
    print("הפלט הזה נלכד")

print("נלכד:", out.getvalue())

🔧 תפיסת stderr או שני הזרמים

תפיסת stderr

@contextmanager
def capture_stderr():
    old_stderr = sys.stderr
    sys.stderr = buffer = io.StringIO()
    try:
        yield buffer
    finally:
        sys.stderr = old_stderr

תפיסת stdout + stderr

@contextmanager
def capture_output():
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = sys.stderr = buffer = io.StringIO()
    try:
        yield buffer
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr

🔀 מצב Tee: תפוס והצג בו זמנית

class Tee(io.StringIO):
    def __init__(self, original):
        super().__init__()
        self.original = original

    def write(self, text):
        self.original.write(text)
        super().write(text)

    def flush(self):
        self.original.flush()

Context managers:

@contextmanager
def capture_stdout_tee():
    old_stdout = sys.stdout
    sys.stdout = tee = Tee(old_stdout)
    try:
        yield tee
    finally:
        sys.stdout = old_stdout

@contextmanager
def capture_output_tee():
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = sys.stderr = tee = Tee(old_stdout)
    try:
        yield tee
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr

דוגמה:

with capture_stdout_tee() as out:
    print("נראה וגם נלכד")

print("תוכן Tee:", out.getvalue())

📦 קובץ מלא: stdout_capture.py

"""
כלי ללכידת stdout ו-stderr באמצעות context managers.
תומך ב:
- תפיסת stdout
- תפיסת stderr
- תפיסה משולבת
- מצב Tee
"""

import sys, io
from contextlib import contextmanager

@contextmanager
def capture_stdout():
    old_stdout = sys.stdout
    sys.stdout = buffer = io.StringIO()
    try: yield buffer
    finally: sys.stdout = old_stdout

@contextmanager
def capture_stderr():
    old_stderr = sys.stderr
    sys.stderr = buffer = io.StringIO()
    try: yield buffer
    finally: sys.stderr = old_stderr

@contextmanager
def capture_output():
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = sys.stderr = buffer = io.StringIO()
    try: yield buffer
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr

class Tee(io.StringIO):
    def __init__(self, original):
        super().__init__()
        self.original = original
    def write(self, text):
        self.original.write(text)
        super().write(text)
    def flush(self):
        self.original.flush()

@contextmanager
def capture_stdout_tee():
    old_stdout = sys.stdout
    sys.stdout = tee = Tee(old_stdout)
    try: yield tee
    finally: sys.stdout = old_stdout

@contextmanager
def capture_output_tee():
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = sys.stderr = tee = Tee(old_stdout)
    try: yield tee
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr

✅ דוגמאות שימוש

from stdout_capture import capture_stdout, capture_output, capture_stdout_tee

def test_func():
    print("שלום מהפונקציה")

# תפיסת stdout
with capture_stdout() as out:
    test_func()
print("stdout נלכד:", out.getvalue())

# תפיסת stdout + stderr
try:
    with capture_output() as out:
        print("משהו")
        raise Exception("אופס!")
except Exception:
    pass
print("פלט נלכד:", out.getvalue())

# תפיסת והצגת פלט בו זמנית
with capture_stdout_tee() as out:
    print("נראה וגם נלכד")
print("תוכן Tee:", out.getvalue())

⚠️ טיפים ומלכודות

1️⃣ פלט גדול מאוד

  • io.StringIO עלול לצרוך הרבה זיכרון.
  • השתמש בקובץ זמני:
import sys, tempfile

with tempfile.TemporaryFile(mode='w+') as tmp:
    old_stdout = sys.stdout
    sys.stdout = tmp
    try:
        print("הרבה נתונים...")
        tmp.seek(0)
        captured = tmp.read()
    finally:
        sys.stdout = old_stdout

2️⃣ פלט תהליכים משניים

  • capture_stdout() לא תופס פלט מתהליכים חיצוניים.
  • השתמש ב-subprocess.PIPE:
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)

3️⃣ Thread-safety

  • sys.stdout ו-sys.stderr הם גלובליים → הימנע מלכוד בו זמנית בכמה threads.

4️⃣ מתי להשתמש ב-Tee

  • דיבוג פונקציות עם לוגינג
  • כלים CLI אינטראקטיביים
  • בדיקות אינטגרציה עם פלט למסוף

5️⃣ שחזור זרמים

  • תמיד שחזר את sys.stdout/sys.stderr ב-finally

6️⃣ הימנע מהשכבות עומק גדולות

  • לכידות מרובות עלולות לערבב פלט. השתמש בקונטקסט אחד לכל תרחיש.

7️⃣ Best Practices בבדיקות

  • Unit tests → capture_stdout() או capture_output()
  • דיבוג ידני → מצב Tee
  • בדוק תוכן ה-buffer, לא תופעות לוואי

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *