2.19. Pisanie modułów

Każdy plik .py jest modułem. Rozdzielenie rozrastającego się skryptu na kilka plików utrzymuje każdy plik krótkim i pozwala współdzielić wspólne funkcje pomocnicze między skryptami.

2.19.1. Rozdzielanie skryptu

Wyciągnij powiązaną grupę funkcji do osobnego pliku:

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

Wyjście:

OpenMV
[ready]

Oba pliki leżą obok siebie w tym samym katalogu. Gdy uruchamia się main.py, import camera_utils raz odczytuje camera_utils.py, wykonuje jego instrukcje najwyższego poziomu i wiąże powstały obiekt modułu z nazwą camera_utils w main. Drugi import camera_utils z dowolnego innego miejsca zwraca ten sam obiekt – moduły są buforowane po pierwszym wczytaniu.

Nazwa modułu pochodzi od jego nazwy pliku, więc camera_utils.py jest importowany jako import camera_utils.

2.19.2. Moduły wieloplikowe (pakiety)

Moduł może też być katalogiem plików, a nie pojedynczym plikiem .py. Nazwa katalogu staje się nazwą modułu, a pliki w środku to jego podmoduły:

camera_utils/
    __init__.py
    text.py
    timing.py

__init__.py to plik, który uruchamia się przy imporcie samego pakietu; może być pusty albo może ponownie eksportować wybrane nazwy z podmodułów. Do podmodułów sięga się za pomocą nazwy z kropką:

import camera_utils.text
from camera_utils.timing import elapsed

camera_utils.text.label("ready")

Wewnątrz pakietu podmoduły mogą sięgać do siebie nawzajem albo poprzez pełną nazwę z kropką, albo poprzez import względny, który używa wiodącej kropki na oznaczenie „tego pakietu”:

camera_utils/timing.py

from . import text             # the sibling submodule

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

Aby zamiast tego wciągnąć konkretną nazwę, podaj ją po nazwie rodzeństwa z kropką:

from .text import label

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

Importy względne utrzymują pakiet samowystarczalnym: zmiana nazwy katalogu pakietu nie wymaga edytowania każdego podmodułu.

Stosuj pakiet, gdy pojedynczy plik urośnie ponad wygodny rozmiar lub gdy zestaw powiązanych modułów powinien być razem pod jedną przestrzenią nazw. Do codziennych skryptów pojedynczy plik .py wystarcza.

2.19.3. Zabezpieczenie __name__

Każdy moduł ma wbudowaną nazwę __name__. Jej wartość zależy od tego, jak plik jest używany:

  • Gdy plik jest uruchamiany bezpośrednio, __name__ przyjmuje wartość napisu "__main__".

  • Gdy plik jest importowany przez inny skrypt, __name__ przyjmuje wartość nazwy modułu – nazwy pliku bez .py.

Idiom, który to wykorzystuje, wygląda tak:

label_util.py

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

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

Wyjście (przy bezpośrednim uruchomieniu):

[self-test]

Gdy ten sam plik jest zamiast tego importowany, __name__ przyjmuje wartość nazwy modułu, więc blok if jest pomijany i nic dodatkowego się nie uruchamia:

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

Stosuj ten wzorzec, aby dołączyć szybki test poprawności lub demo do pliku biblioteki, nie zakłócając skryptów, które go importują.