2.19. Viết modules

Bất kỳ tệp .py nào cũng là một module. Chia một tập lệnh đang phát triển thành một vài tệp giữ cho mỗi tệp ngắn gọn và cho phép các helper chung được chia sẻ giữa các tập lệnh.

2.19.1. Chia nhỏ một tập lệnh

Kéo một nhóm hàm liên quan ra thành tệp riêng:

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"))

Đầu ra:

OpenMV
[ready]

Hai tệp nằm cạnh nhau trong cùng thư mục. Khi main.py chạy, import camera_utils đọc camera_utils.py một lần, thực thi các câu lệnh cấp cao nhất của nó, và gắn đối tượng module kết quả vào tên camera_utils trong main. Một import camera_utils thứ hai từ bất kỳ đâu khác trả về cùng một đối tượng -- các module được lưu vào cache sau lần tải đầu tiên.

Tên của một module đến từ tên tệp của nó, vì vậy camera_utils.py được import bằng import camera_utils.

2.19.2. Modules nhiều tệp (packages)

Một module cũng có thể là thư mục của các tệp thay vì một .py đơn lẻ. Tên thư mục trở thành tên module, và các tệp bên trong là các submodule của nó:

camera_utils/
    __init__.py
    text.py
    timing.py

__init__.py là tệp chạy khi bản thân package được import; nó có thể rỗng, hoặc nó có thể tái xuất các tên được chọn từ các submodule. Các submodule được truy cập bằng tên có dấu chấm:

import camera_utils.text
from camera_utils.timing import elapsed

camera_utils.text.label("ready")

Bên trong package, các submodule có thể tiếp cận lẫn nhau thông qua tên có dấu chấm đầy đủ hoặc thông qua relative import sử dụng dấu chấm đầu để có nghĩa là "package này":

camera_utils/timing.py

from . import text             # the sibling submodule

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

Để đưa vào một tên cụ thể thay thế, hãy đặt tên nó sau sibling có dấu chấm:

from .text import label

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

Relative imports giữ cho một package tự chứa: đổi tên thư mục package không yêu cầu chỉnh sửa mọi submodule.

Sử dụng package khi một tệp đơn phát triển quá kích thước thoải mái, hoặc khi một tập hợp các module liên quan thuộc về cùng nhau trong một không gian tên. Đối với các tập lệnh thông thường, một tệp .py đơn là đủ.

2.19.3. Guard __name__

Mọi module đều có tên tích hợp sẵn __name__. Giá trị của nó phụ thuộc vào cách tệp đang được sử dụng:

  • Khi tệp được chạy trực tiếp, __name__ được đặt thành chuỗi "__main__".

  • Khi tệp được import bởi tập lệnh khác, __name__ được đặt thành tên module -- tên tệp không có .py.

Idiom sử dụng điều này là:

label_util.py

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

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

Đầu ra (khi chạy trực tiếp):

[self-test]

Khi cùng một tệp được import thay thế, __name__ được đặt thành tên module, nên khối if bị bỏ qua và không có gì thêm chạy:

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

Sử dụng mẫu này để gắn một bài kiểm tra nhanh hoặc demo vào tệp thư viện mà không làm phiền các tập lệnh import nó.