2.19. การเขียนโมดูล¶
ไฟล์ .py ใดๆ ก็คือโมดูล การแบ่งสคริปต์ที่กำลังเติบโตออกเป็นหลายไฟล์ทำให้แต่ละไฟล์สั้นลงและช่วยให้ helper ทั่วไปสามารถแบ่งปันระหว่างสคริปต์ได้
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 ครั้งเดียว ดำเนินการ statement ระดับบนสุด และผูกอ็อบเจ็กต์โมดูลที่ได้กับชื่อ camera_utils ใน main import camera_utils ครั้งที่สองจากที่ไหนก็ตามจะคืนอ็อบเจ็กต์เดียวกัน -- โมดูลถูก cache หลังจากโหลดครั้งแรก
ชื่อของโมดูลมาจากชื่อไฟล์ ดังนั้น camera_utils.py จะถูก import เป็น import camera_utils
2.19.2. โมดูลหลายไฟล์ (packages)¶
โมดูลยังสามารถเป็น ไดเรกทอรี ของไฟล์แทนที่จะเป็นไฟล์ .py เดียว ชื่อของไดเรกทอรีกลายเป็นชื่อโมดูล และไฟล์ภายในคือ submodules ของมัน:
camera_utils/
__init__.py
text.py
timing.py
__init__.py คือไฟล์ที่ทำงานเมื่อ package เองถูก import; มันสามารถว่างเปล่า หรือสามารถ re-export ชื่อที่เลือกจาก submodules Submodules ถูกเข้าถึงด้วยชื่อแบบจุด:
import camera_utils.text
from camera_utils.timing import elapsed
camera_utils.text.label("ready")
ภายใน package, submodules สามารถเข้าถึงกันและกันได้ทั้งด้วยชื่อแบบจุดแบบเต็ม หรือด้วย relative import ที่ใช้จุดนำหน้าเพื่อหมายถึง "package นี้":
camera_utils/timing.py
from . import text # the sibling submodule
def stamp(value):
return text.label(str(value))
เพื่อดึงชื่อเฉพาะแทน ให้ระบุชื่อหลัง sibling แบบจุด:
from .text import label
def stamp(value):
return label(str(value))
Relative imports ทำให้ package พึ่งพาตัวเองได้: การเปลี่ยนชื่อไดเรกทอรี package ไม่จำเป็นต้องแก้ไขทุก submodule
ใช้ package เมื่อไฟล์เดียวเติบโตเกินขนาดที่สะดวก หรือเมื่อชุดของโมดูลที่เกี่ยวข้องเป็นของกันและกันภายใต้ namespace เดียว สำหรับสคริปต์ประจำวันไฟล์ .py เดียวก็เพียงพอแล้ว
2.19.3. การป้องกันด้วย __name__¶
ทุกโมดูลมีชื่อในตัว __name__ ค่าของมันขึ้นอยู่กับวิธีที่ไฟล์ถูกใช้งาน:
เมื่อไฟล์ รันโดยตรง
__name__จะถูกตั้งค่าเป็น string"__main__"เมื่อไฟล์ ถูก import โดยสคริปต์อื่น
__name__จะถูกตั้งค่าเป็นชื่อโมดูล -- ชื่อไฟล์ไม่รวม.py
idiom ที่ใช้สิ่งนี้คือ:
label_util.py
def label(text):
return "[" + text + "]"
if __name__ == "__main__":
print(label("self-test"))
ผลลัพธ์ (เมื่อรันโดยตรง):
[self-test]
เมื่อไฟล์เดียวกัน ถูก import แทน __name__ จะถูกตั้งค่าเป็นชื่อโมดูล ดังนั้น block if จะถูกข้ามและไม่มีอะไรเพิ่มเติมทำงาน:
>>> import label_util
>>> label_util.__name__
'label_util'
ใช้รูปแบบนี้เพื่อแนบการทดสอบอย่างเร็วหรือ demo ไปกับไฟล์ library โดยไม่รบกวนสคริปต์ที่ import มัน