2.19. כתיבת מודולים#

כל קובץ .py הוא מודול. פיצול סקריפט גדל בין כמה קבצים שומר על כל קובץ קצר ומאפשר לעוזרים משותפים להיות משותפים בין סקריפטים.

2.19.1. פיצול סקריפט#

משכו קבוצה קשורה של פונקציות אל קובץ משלהן:

camera_utils.py

def banner():
    print("OpenMV")

def label(text):
    return "[" + text + "]"

main.py

import camera_utils

camera_utils.banner()
print(camera_utils.label("ready"))

פלט:

OpenMV
[ready]

שני הקבצים יושבים זה לצד זה באותה תיקייה. כאשר main.py רץ, import camera_utils קורא את camera_utils.py פעם אחת, מריץ את משפטיו ברמה העליונה, וקושר את אובייקט המודול המתקבל לשם camera_utils ב-main. import camera_utils שני מכל מקום אחר מחזיר את אותו אובייקט – מודולים נשמרים במטמון לאחר טעינתם הראשונה.

שם של מודול מגיע משם הקובץ שלו, כך ש-camera_utils.py מיובא כ-import camera_utils.

2.19.2. מודולים מרובי-קבצים (חבילות)#

מודול יכול להיות גם תיקייה של קבצים ולא קובץ .py יחיד. שם התיקייה הופך לשם המודול, והקבצים שבתוכה הם תת-המודולים שלו:

camera_utils/
    __init__.py
    text.py
    timing.py

__init__.py הוא הקובץ שרץ כאשר החבילה עצמה מיובאת; הוא יכול להיות ריק, או שהוא יכול לייצא מחדש שמות נבחרים מתת-המודולים. תת-המודולים מושגים באמצעות שם מנוקד:

import camera_utils.text
from camera_utils.timing import elapsed

camera_utils.text.label("ready")

בתוך החבילה, תת-מודולים יכולים להגיע זה לזה באמצעות השם המנוקד המלא או באמצעות ייבוא יחסי המשתמש בנקודה מובילה כדי לציין ”חבילה זו“:

camera_utils/timing.py

from . import text             # the sibling submodule

def stamp(value):
    return text.label(str(value))

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

from .text import label

def stamp(value):
    return label(str(value))

ייבוא יחסי שומר על חבילה עצמאית: שינוי שם תיקיית החבילה אינו דורש עריכה של כל תת-מודול.

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

2.19.3. מגן ה-__name__#

לכל מודול יש שם מובנה __name__. ערכו תלוי באופן שבו הקובץ נמצא בשימוש:

  • כאשר הקובץ רץ ישירות, __name__ מוגדר למחרוזת "__main__".

  • כאשר הקובץ מיובא על ידי סקריפט אחר, __name__ מוגדר לשם המודול – שם הקובץ ללא .py.

הניב המשתמש בכך הוא:

label_util.py

def label(text):
    return "[" + text + "]"

if __name__ == "__main__":
    print(label("self-test"))

פלט (כאשר רץ ישירות):

[self-test]

כאשר אותו קובץ מיובא במקום זאת, __name__ מוגדר לשם המודול, כך שבלוק ה-if מדולג ושום דבר נוסף אינו רץ:

>>> import label_util
>>> label_util.__name__
'label_util'

השתמשו בדפוס כדי לצרף בדיקת עשן או הדגמה מהירה לקובץ ספרייה מבלי להפריע לסקריפטים שמייבאים אותו.