יום 2 - שאלה על התשובה ב-Stackoverflow

תגיות:

היי,
בחלק האחרון של התשובה בקישור (בחלק Food for Thought), לא הבנתי את הפלטים של הקבצים foo2 ו-foo3.
בפלט של foo2:
t1
m1
a1
t1 <== פה, למה הוא קפץ ל-t1? ציפיתי שהוא ימשיך ל-a2 ואז b כמו שמופיע אח"כ.
t2
a2
b
a3
m2
t2

2 לייקים

ממש סחטיין על ההתעמקות, מרשים לטובה :slight_smile:
לא התעצלתי וכתבתי לך פה תשובה סופר מפורטת.
ועדיין, אני ממליץ לך לפני שתקרא אותך לשלוף את הדיבאגר הקרוב לביתך ולעבור איתו שורה שורה על הקוד.
זה יעזור לך להבין הרבה יותר טוב בעצמך איך פייתון עובדת.


לנוחות באי האשכול, מדביק כאן את הקוד:

# Suppose this is foo2.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

def functionA():
    print("a1")
    from foo2 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
if __name__ == "__main__":
    print("m1")
    functionA()
    print("m2")
print("t2")
ואת הפלט...
t1
m1
a1
t1
t2
a2
b
a3
m2
t2
  1. מתחילים ב־t1, כי פייתון עדיין לא נכנסה לאף פונקציה.
  2. כיוון שהמודול שהורץ מלכתחילה הוא foo2, והוא נטען ישירות בעזרת הרצת python foo2.py (כך ה־IDE מפעיל אותו) ולא דרך import, יודפס גם m1 ותקרא הפונקציה functionA.
  3. היא תדפיס, בתורה, a1, ועכשיו יקרה החלק המעניין – from foo2 import functionB.
  4. כשאנחנו עושים from x import Y פייתון עדיין טוענת את כל המודול, כדי לא לפספס הגדרה של משתנים חשובים ב־scope הגלובאלי או פעולות (כתיבה לקובץ, תקשורת עם שרת כלשהו) שהיא צריכה לבצע.
    הפונקציה functionB כן יובאה כבר למקום בו כתוב from foo2 import functionB, אבל פייתון תמשיך לקרוא את המודול שעשינו לו import עד סופו. לכן t1 תודפס.
  5. אבל __name__ עכשיו הוא foo2, ולא __main__ (כי טענו את המודול בעזרת import ולא בעזרת הרצת python), ולכן נדלג על הקוד המוזח ב־if.
    משם ממשיכים להריץ עד סוף הקובץ (כך עובד ייבוא), ולכן נדפיס גם t2.
  6. סיימנו לטעון את functionB. עכשיו מדפיסים את a2, כי זו הפעולה הבאה אחרי from foo2 import functionB.
  7. מריצים את functionB, ולכן מדפיסים את b.
  8. סיימנו. ממשיכים ומדפיסים את a3.
  9. סיימנו את הקריאה ל־functionA, ולכן ממשיכים להדפיס את m2.
  10. סיימנו להיות בתוך ה־if – מדפיסים את t2.
14 לייקים

עכשיו הבנתי! והבנתי גם את foo3 :grin:
עבדתי עם הדיבאגר, עכשיו אני מבין גם למה הוא חזר לתחילת הקוד מיד אחרי השורה from foo2 import functionB, זה מה שכתבת בסעיף 4.
תודה!

2 לייקים

אחלה מקור! השכלתי.
מאוד מפתיע החלק של הייבוא העצמי.

לייק 1

היי,
משהו אחד שלא בדיוק ברור לי ב-foo3.
מאחר ואין את התנאי שבודק את ה-main כמו ב-foo2, למה לא נוצרת לנו כאן לולאה אין סופית של קריאות?

הרי אנחנו קוראים ל-functionA שקוראת ל-functionB שכמובן ללא תנאי ה-main בקובץ נוצרת קריאה נוספת
ל-functionA.

(אני מניח שהוא יודע להתמודד עם זה ולכן לא נוצרת לנו לולאה אין סופית - אבל אשמח להבהרה)

הרצתי את ה-debugger ובזמן הקריאה ל-functionB ראיתי משתנה count שכנראה בודק את כמות הקריאות לקובץ - אבל אני לא סגור עלזה, כמו כן היה שם משתנה נוסף של recursive שהיה false.

הקוד:

import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

def functionA():
    print("a1")
    from foo3 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
print("m1")
functionA()
print("m2")
print("t2")

תודה לעונים :slight_smile:

לא נוצרת לולאה אין סופית מכיוון שכשמייבאים מודול הוא מתרחש פעם אחת בזמן היבוא שלו…

ציטוט מתוך ‘real python’:

When you import a module, what really happens is that you load its contents for later access and use. The interesting thing about this process is that import runs the code as its final step.

When the module contains only classes, functions, variables, and constants definitions, you probably won’t be aware that the code was actually run, but when the module includes calls to functions, methods, or other statements that generate visible results, then you’ll witness its execution.

This provides you with another option to run Python scripts:

import hello
Hello World!

You’ll have to note that this option works only once per session. After the first import , successive import executions do nothing, even if you modify the content of the module. This is because import operations are expensive and therefore run only once. Here’s an example:

import hello  # Do nothing
import hello  # Do nothing again

These two import operations do nothing, because Python knows that hello has already been imported.

להרחבה: https://realpython.com/run-python-scripts/

3 לייקים

מעולה, תודה רבה על התשובה אפרת :slight_smile:

לייק 1