6. Mô-đun

Nếu bạn thoát khỏi trình thông dịch Python và nhập lại, các định nghĩa bạn đã tạo (hàm và biến) sẽ bị mất. Do đó, nếu bạn muốn viết một chương trình dài hơn một chút, tốt hơn hết bạn nên sử dụng trình soạn thảo văn bản để chuẩn bị đầu vào cho trình thông dịch và chạy nó với tệp đó làm đầu vào. Điều này được gọi là tạo script. Khi chương trình của bạn dài hơn, bạn có thể muốn chia nó thành nhiều tệp để bảo trì dễ dàng hơn. Bạn cũng có thể muốn sử dụng một hàm tiện dụng mà bạn đã viết trong một số chương trình mà không cần sao chép định nghĩa của nó vào từng chương trình.

Để hỗ trợ điều này, Python có cách đưa các định nghĩa vào một tệp và sử dụng chúng trong tập lệnh hoặc trong phiên bản tương tác của trình thông dịch. Tệp như vậy được gọi là module; các định nghĩa từ một mô-đun có thể được chuyển sang imported vào các mô-đun khác hoặc vào mô-đun main (tập hợp các biến mà bạn có quyền truy cập trong một tập lệnh được thực thi ở cấp cao nhất và ở chế độ máy tính).

Mô-đun là một tệp chứa các định nghĩa và câu lệnh Python. Tên tệp là tên mô-đun có thêm hậu tố .py. Trong một mô-đun, tên của mô-đun (dưới dạng chuỗi) có sẵn dưới dạng giá trị của biến toàn cục __name__. Ví dụ: sử dụng trình soạn thảo văn bản yêu thích của bạn để tạo một tệp có tên fibo.py trong thư mục hiện tại với nội dung sau:

-đun số # Fibonacci

def fib(n):
    """Viết chuỗi Fibonacci lên đến n."""
    a, b = 0, 1
    trong khi a < n:
        in(a, end=' ')
        a, b = b, a+b
    in()

def fib2(n):
    """Trả về chuỗi Fibonacci lên đến n."""
    kết quả = []
    a, b = 0, 1
    trong khi a < n:
        result.append(a)
        a, b = b, a+b
    kết quả trả về

Bây giờ hãy nhập trình thông dịch Python và nhập mô-đun này bằng lệnh sau

>>> nhập khẩu fibo

Điều này không thêm trực tiếp tên của các hàm được xác định trong fibo vào namespace hiện tại (xem Phạm vi và không gian tên Python để biết thêm chi tiết); nó chỉ thêm tên mô-đun fibo vào đó. Sử dụng tên mô-đun, bạn có thể truy cập các chức năng

>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'xơ'

Nếu bạn có ý định sử dụng một hàm thường xuyên, bạn có thể gán nó cho một tên cục bộ:

>>> fib = fibo.fib
>>> sợi(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1. Thông tin thêm về Mô-đun

Một mô-đun có thể chứa các câu lệnh thực thi cũng như các định nghĩa hàm. Những câu lệnh này nhằm mục đích khởi tạo mô-đun. Chúng chỉ được thực thi vào thời điểm first gặp tên mô-đun trong câu lệnh nhập. [1] (Chúng cũng được chạy nếu tệp được thực thi dưới dạng tập lệnh.)

Mỗi mô-đun có không gian tên riêng, được sử dụng làm không gian tên chung bởi tất cả các hàm được xác định trong mô-đun. Do đó, tác giả của mô-đun có thể sử dụng các biến toàn cục trong mô-đun mà không phải lo lắng về các xung đột ngẫu nhiên với các biến toàn cục của người dùng. Mặt khác, nếu bạn biết mình đang làm gì, bạn có thể chạm vào các biến toàn cục của mô-đun với cùng ký hiệu được sử dụng để chỉ các chức năng của nó, modname.itemname.

Các mô-đun có thể nhập các mô-đun khác. Theo thông lệ nhưng không bắt buộc phải đặt tất cả các câu lệnh import ở đầu mô-đun (hoặc tập lệnh, đối với vấn đề đó). Tên mô-đun đã nhập, nếu được đặt ở cấp cao nhất của mô-đun (bên ngoài bất kỳ chức năng hoặc lớp nào), sẽ được thêm vào không gian tên chung của mô-đun.

Có một biến thể của câu lệnh import nhập tên từ mô-đun trực tiếp vào không gian tên của mô-đun đang nhập. Ví dụ:

>>> từ fibo nhập fib, fib2
>>> sợi(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Điều này không giới thiệu tên mô-đun mà từ đó quá trình nhập được thực hiện trong không gian tên cục bộ (vì vậy trong ví dụ này, fibo không được xác định).

Thậm chí còn có một biến thể để nhập tất cả các tên mà mô-đun xác định

>>> từ nhập fibo *
>>> sợi(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Điều này sẽ nhập tất cả các tên ngoại trừ những tên bắt đầu bằng dấu gạch dưới (_). Trong hầu hết các trường hợp, các lập trình viên Python không sử dụng tiện ích này vì nó đưa một tập hợp tên không xác định vào trình thông dịch, có thể ẩn một số thứ bạn đã xác định.

Lưu ý rằng nhìn chung, việc nhập * từ một mô-đun hoặc gói không được tán thành vì nó thường gây ra mã khó đọc. Tuy nhiên, bạn có thể sử dụng nó để tiết kiệm việc gõ phím trong các phiên tương tác.

Nếu tên mô-đun được theo sau bởi as thì tên theo sau as sẽ được liên kết trực tiếp với mô-đun đã nhập.

>>> nhập fibo dưới dạng fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Đây là cách nhập mô-đun một cách hiệu quả theo cách tương tự như import fibo sẽ thực hiện, với điểm khác biệt duy nhất là nó có sẵn dưới dạng fib.

Nó cũng có thể được sử dụng khi sử dụng from với các hiệu ứng tương tự:

>>> từ fibo nhập fib dưới dạng fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Ghi chú

Vì lý do hiệu quả, mỗi mô-đun chỉ được nhập một lần cho mỗi phiên thông dịch. Do đó, nếu thay đổi mô-đun của mình, bạn phải khởi động lại trình thông dịch -- hoặc, nếu đó chỉ là một mô-đun bạn muốn kiểm tra tính tương tác, hãy sử dụng importlib.reload(), ví dụ: import importlib; importlib.reload(modulename).

6.1.1. Thực thi các mô-đun dưới dạng tập lệnh

Khi bạn chạy mô-đun Python với

python fibo.py <đối số>

mã trong mô-đun sẽ được thực thi, giống như khi bạn nhập nó, nhưng với __name__ được đặt thành "__main__". Điều đó có nghĩa là bằng cách thêm mã này vào cuối mô-đun của bạn:

nếu __name__ == "__main__":
    hệ thống nhập khẩu
    fib(int(sys.argv[1]))

bạn có thể làm cho tệp có thể sử dụng được dưới dạng tập lệnh cũng như mô-đun có thể nhập được, vì mã phân tích dòng lệnh chỉ chạy nếu mô-đun được thực thi dưới dạng tệp "chính":

$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34

Nếu mô-đun được nhập, mã sẽ không chạy:

>>> nhập khẩu fibo
>>>

Điều này thường được sử dụng để cung cấp giao diện người dùng thuận tiện cho mô-đun hoặc cho mục đích thử nghiệm (chạy mô-đun dưới dạng tập lệnh thực thi bộ thử nghiệm).

6.1.2. Đường dẫn tìm kiếm mô-đun

Khi một mô-đun có tên spam được nhập, trước tiên trình thông dịch sẽ tìm kiếm mô-đun tích hợp có tên đó. Các tên mô-đun này được liệt kê trong sys.builtin_module_names. Nếu không tìm thấy, nó sẽ tìm kiếm tệp có tên spam.py trong danh sách các thư mục được cung cấp bởi biến sys.path. sys.path được khởi tạo từ các vị trí sau:

  • Thư mục chứa tập lệnh đầu vào (hoặc thư mục hiện tại khi không có tệp nào được chỉ định).

  • PYTHONPATH (danh sách tên thư mục, có cùng cú pháp với biến shell PATH).

  • Mặc định phụ thuộc vào cài đặt (theo quy ước bao gồm thư mục site-packages, được xử lý bởi mô-đun site).

Thông tin chi tiết có tại Việc khởi tạo đường dẫn tìm kiếm mô-đun sys.path.

Ghi chú

Trên các hệ thống tệp hỗ trợ liên kết tượng trưng, ​​​​thư mục chứa tập lệnh đầu vào được tính toán sau khi theo dõi liên kết tượng trưng. Nói cách khác, thư mục chứa liên kết tượng trưng là not được thêm vào đường dẫn tìm kiếm mô-đun.

Sau khi khởi tạo, các chương trình Python có thể sửa đổi sys.path. Thư mục chứa tập lệnh đang chạy được đặt ở đầu đường dẫn tìm kiếm, trước đường dẫn thư viện chuẩn. Điều này có nghĩa là các tập lệnh trong thư mục đó sẽ được tải thay vì các mô-đun cùng tên trong thư mục thư viện. Đây là một lỗi trừ khi có ý định thay thế. Xem phần Mô-đun tiêu chuẩn để biết thêm thông tin.

6.1.3. Các tệp Python "được biên dịch"

Để tăng tốc độ tải mô-đun, Python lưu trữ phiên bản đã biên dịch của từng mô-đun trong thư mục __pycache__ dưới tên module.version.pyc, trong đó phiên bản mã hóa định dạng của tệp đã biên dịch; nó thường chứa số phiên bản Python. Ví dụ: trong bản phát hành CPython 3.3, phiên bản đã biên dịch của spam.py sẽ được lưu vào bộ đệm dưới dạng __pycache__/spam.cpython-33.pyc. Quy ước đặt tên này cho phép các mô-đun được biên dịch từ các bản phát hành khác nhau và các phiên bản Python khác nhau cùng tồn tại.

Python kiểm tra ngày sửa đổi của nguồn so với phiên bản đã biên dịch để xem liệu nó có lỗi thời và cần được biên dịch lại hay không. Đây là một quá trình hoàn toàn tự động. Ngoài ra, các mô-đun được biên dịch độc lập với nền tảng, do đó, cùng một thư viện có thể được chia sẻ giữa các hệ thống có kiến ​​trúc khác nhau.

Python không kiểm tra bộ đệm trong hai trường hợp. Đầu tiên, nó luôn biên dịch lại và không lưu trữ kết quả cho mô-đun được tải trực tiếp từ dòng lệnh. Thứ hai, nó không kiểm tra bộ đệm nếu không có mô-đun nguồn. Để hỗ trợ phân phối không phải nguồn (chỉ được biên dịch), mô-đun đã biên dịch phải nằm trong thư mục nguồn và không được có mô-đun nguồn.

Một số lời khuyên dành cho chuyên gia:

  • Bạn có thể sử dụng các khóa chuyển -O hoặc -OO trên lệnh Python để giảm kích thước của mô-đun đã biên dịch. Công tắc -O loại bỏ các câu lệnh khẳng định, công tắc -OO loại bỏ cả câu lệnh khẳng định và chuỗi __doc__. Vì một số chương trình có thể phụ thuộc vào việc có sẵn những tính năng này nên bạn chỉ nên sử dụng tùy chọn này nếu bạn biết mình đang làm gì. Các mô-đun "Tối ưu hóa" có thẻ opt- và thường nhỏ hơn. Các bản phát hành trong tương lai có thể thay đổi tác động của việc tối ưu hóa.

  • Một chương trình không chạy nhanh hơn khi nó được đọc từ tệp .pyc so với khi nó được đọc từ tệp .py; điều duy nhất nhanh hơn về các tệp .pyc là tốc độ tải chúng.

  • Mô-đun compileall có thể tạo tệp .pyc cho tất cả các mô-đun trong một thư mục.

  • Có nhiều chi tiết hơn về quy trình này, bao gồm biểu đồ luồng các quyết định, trong PEP 3147.

6.2. Mô-đun tiêu chuẩn

Python đi kèm với một thư viện gồm các mô-đun tiêu chuẩn, được mô tả trong một tài liệu riêng, Tài liệu tham khảo thư viện Python ("Tham khảo thư viện" sau đây). Một số mô-đun được tích hợp vào trình thông dịch; những điều này cung cấp quyền truy cập vào các hoạt động không phải là một phần cốt lõi của ngôn ngữ nhưng vẫn được tích hợp sẵn, để đạt hiệu quả hoặc để cung cấp quyền truy cập vào các nguyên thủy của hệ điều hành như lệnh gọi hệ thống. Tập hợp các mô-đun như vậy là một tùy chọn cấu hình cũng phụ thuộc vào nền tảng cơ bản. Ví dụ: mô-đun winreg chỉ được cung cấp trên hệ thống Windows. Một mô-đun cụ thể đáng được chú ý: sys, được tích hợp trong mọi trình thông dịch Python. Các biến sys.ps1sys.ps2 xác định các chuỗi được sử dụng làm lời nhắc chính và phụ:

>>> nhập hệ thống
>>> sys.ps1
'>>>'
>>> sys.ps2
'... '
>>> sys.ps1 = 'C>'
C> print('Thật tệ!')
Quá tệ!
C>

Hai biến này chỉ được xác định nếu trình thông dịch ở chế độ tương tác.

Biến sys.path là danh sách các chuỗi xác định đường dẫn tìm kiếm mô-đun của trình thông dịch. Nó được khởi tạo thành đường dẫn mặc định được lấy từ biến môi trường PYTHONPATH hoặc từ mặc định tích hợp nếu PYTHONPATH không được đặt. Bạn có thể sửa đổi nó bằng cách sử dụng các thao tác danh sách tiêu chuẩn:

>>> nhập hệ thống
>>> sys.path.append('/ufs/guido/lib/python')

6.3. Chức năng dir()

Hàm tích hợp dir() được sử dụng để tìm ra tên mà mô-đun xác định. Nó trả về một danh sách các chuỗi được sắp xếp

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
 '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
 '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
 '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
 'warnoptions']

Không có đối số, dir() liệt kê các tên bạn đã xác định hiện tại:

>>> a = [1, 2, 3, 4, 5]
>>> nhập khẩu fibo
>>> fib = fibo.fib
>>> thư mục()
['__buildins__', '__name__', 'a', 'fib', 'fibo', 'sys']

Lưu ý rằng nó liệt kê tất cả các loại tên: biến, mô-đun, hàm, v.v.

dir() không liệt kê tên các hàm và biến có sẵn. Nếu bạn muốn có danh sách những thứ đó, chúng được xác định trong mô-đun tiêu chuẩn builtins:

>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
 'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',
 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
 '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',
 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',
 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',
 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',
 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',
 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',
 'zip']

6.4. Gói

Các gói là một cách cấu trúc không gian tên mô-đun của Python bằng cách sử dụng "tên mô-đun chấm". Ví dụ: tên mô-đun A.B chỉ định một mô-đun con có tên B trong gói có tên A. Giống như việc sử dụng các mô-đun giúp tác giả của các mô-đun khác nhau khỏi phải lo lắng về tên biến toàn cục của nhau, việc sử dụng tên mô-đun có dấu chấm giúp tác giả của các gói nhiều mô-đun như NumPy hoặc Pillow không phải lo lắng về tên mô-đun của nhau.

Giả sử bạn muốn thiết kế một tập hợp các mô-đun ("gói") để xử lý thống nhất các tệp âm thanh và dữ liệu âm thanh. Có nhiều định dạng tệp âm thanh khác nhau (thường được phần mở rộng của chúng nhận dạng, ví dụ: .wav, .aiff, .au), vì vậy bạn có thể cần tạo và duy trì bộ sưu tập mô-đun ngày càng tăng để chuyển đổi giữa các định dạng tệp khác nhau. Ngoài ra còn có nhiều thao tác khác nhau mà bạn có thể muốn thực hiện trên dữ liệu âm thanh (chẳng hạn như trộn, thêm tiếng vang, áp dụng chức năng bộ chỉnh âm, tạo hiệu ứng âm thanh nổi nhân tạo), do đó, ngoài ra, bạn sẽ viết một dòng mô-đun không bao giờ kết thúc để thực hiện các thao tác này. Đây là cấu trúc có thể có cho gói của bạn (được biểu thị dưới dạng hệ thống tệp phân cấp):

gói âm thanh/ cấp cao nhất
      __init__.py Khởi tạo gói âm thanh
      định dạng/Gói con để chuyển đổi định dạng tệp
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      hiệu ứng/Gói phụ cho hiệu ứng âm thanh
              __init__.py
              echo.py
              bao quanh.py
              đảo ngược.py
              ...
      bộ lọc/Gói phụ cho bộ lọc
              __init__.py
              bộ chỉnh âm.py
              vocode.py
              karaoke.py
              ...

Khi nhập gói, Python tìm kiếm qua các thư mục trên sys.path để tìm thư mục con của gói.

Cần có tệp __init__.py để Python xử lý các thư mục chứa tệp dưới dạng gói (trừ khi sử dụng namespace package, một tính năng tương đối nâng cao). Điều này ngăn các thư mục có tên chung, chẳng hạn như string, vô tình ẩn các mô-đun hợp lệ xuất hiện sau này trên đường dẫn tìm kiếm mô-đun. Trong trường hợp đơn giản nhất, __init__.py có thể chỉ là một tệp trống nhưng nó cũng có thể thực thi mã khởi tạo cho gói hoặc đặt biến __all__, được mô tả sau.

Người dùng gói có thể nhập các mô-đun riêng lẻ từ gói, ví dụ:

nhập sound.effect.echo

Điều này tải mô-đun con sound.effects.echo. Nó phải được tham chiếu với tên đầy đủ của nó.

sound.effect.echo.echofilter(đầu vào, đầu ra, độ trễ=0,7, atten=4)

Một cách khác để nhập mô-đun con là:

từ sound.effect nhập tiếng vang

Thao tác này cũng tải mô-đun con echo và làm cho nó khả dụng mà không cần tiền tố gói, vì vậy nó có thể được sử dụng như sau:

echo.echofilter(đầu vào, đầu ra, độ trễ=0,7, atten=4)

Một biến thể khác là nhập trực tiếp hàm hoặc biến mong muốn

từ sound.effect.echo nhập bộ lọc tiếng vang

Một lần nữa, thao tác này sẽ tải mô-đun con echo, nhưng điều này làm cho chức năng echofilter() của nó có sẵn trực tiếp:

bộ lọc tiếng vang(đầu vào, đầu ra, độ trễ=0,7, atten=4)

Lưu ý rằng khi sử dụng from package import item, mục này có thể là mô-đun con (hoặc gói con) của gói hoặc một số tên khác được xác định trong gói, như hàm, lớp hoặc biến. Câu lệnh import trước tiên kiểm tra xem mục đó có được xác định trong gói hay không; nếu không, nó sẽ coi đó là một mô-đun và cố gắng tải nó. Nếu không tìm thấy nó, một ngoại lệ ImportError sẽ xuất hiện.

Ngược lại, khi sử dụng cú pháp như import item.subitem.subsubitem, mỗi mục ngoại trừ mục cuối cùng phải là một gói; mục cuối cùng có thể là một mô-đun hoặc một gói nhưng không thể là lớp, hàm hoặc biến được xác định trong mục trước đó.

6.4.1. Nhập * từ một gói

Bây giờ điều gì xảy ra khi người dùng viết from sound.effects import *? Lý tưởng nhất là người ta hy vọng rằng bằng cách nào đó điều này sẽ đi vào hệ thống tập tin, tìm thấy các mô-đun con nào có trong gói và nhập tất cả chúng. Quá trình này có thể mất nhiều thời gian và việc nhập mô-đun phụ có thể gây ra những tác dụng phụ không mong muốn mà chỉ xảy ra khi mô-đun phụ được nhập một cách rõ ràng.

Giải pháp duy nhất là tác giả gói cung cấp chỉ mục rõ ràng của gói. Câu lệnh import sử dụng quy ước sau: nếu mã __init__.py của gói xác định một danh sách có tên __all__, thì danh sách đó sẽ được coi là danh sách các tên mô-đun cần được nhập khi gặp from package import *. Tác giả gói có trách nhiệm cập nhật danh sách này khi phiên bản mới của gói được phát hành. Tác giả gói cũng có thể quyết định không hỗ trợ nó nếu họ không thấy việc nhập * từ gói của họ không được sử dụng. Ví dụ: tệp sound/effects/__init__.py có thể chứa mã sau:

__all__ = ["tiếng vang", "vòm", "đảo ngược"]

Điều này có nghĩa là from sound.effects import * sẽ nhập ba mô-đun con được đặt tên của gói sound.effects.

Xin lưu ý rằng các mô-đun con có thể bị che khuất bởi các tên được xác định cục bộ. Ví dụ: nếu bạn đã thêm hàm reverse vào tệp sound/effects/__init__.py thì from sound.effects import * sẽ chỉ nhập hai mô-đun con echosurround, nhưng not sẽ nhập mô-đun con reverse, vì nó bị che khuất bởi hàm reverse được xác định cục bộ:

__tất cả__ = [
    "echo", # refers vào tệp 'echo.py'
    "vòm", # refers vào tệp 'sround.py'
    "đảo ngược", # !!! bây giờ đề cập đến chức năng 'đảo ngược' !!!
]

def Reverse(msg: str): # <-- tên này che mờ mô-đun con 'reverse.py'
    return msg[::-1] #     in trường hợp 'from sound.effect import *'

Nếu __all__ không được xác định, câu lệnh from sound.effects import * sẽ not nhập tất cả các mô-đun con từ gói sound.effects vào không gian tên hiện tại; nó chỉ đảm bảo rằng gói sound.effects đã được nhập (có thể chạy bất kỳ mã khởi tạo nào trong __init__.py) và sau đó nhập bất kỳ tên nào được xác định trong gói. Điều này bao gồm mọi tên được xác định (và các mô-đun con được tải rõ ràng) bởi __init__.py. Nó cũng bao gồm mọi mô-đun con của gói đã được tải rõ ràng bởi các câu lệnh import trước đó. Hãy xem xét mã này:

nhập sound.effect.echo
nhập sound.effect.sround
từ nhập sound.effect *

Trong ví dụ này, các mô-đun echosurround được nhập vào không gian tên hiện tại vì chúng được xác định trong gói sound.effects khi câu lệnh from...import được thực thi. (Điều này cũng hoạt động khi __all__ được xác định.)

Mặc dù một số mô-đun nhất định được thiết kế để chỉ xuất các tên tuân theo các mẫu nhất định khi bạn sử dụng import *, nhưng nó vẫn được coi là phương pháp không tốt trong mã sản xuất.

Hãy nhớ rằng, không có gì sai khi sử dụng from package import specific_submodule! Trên thực tế, đây là ký hiệu được khuyến nghị trừ khi mô-đun nhập cần sử dụng các mô-đun con có cùng tên từ các gói khác nhau.

6.4.2. Tài liệu tham khảo trong gói

Khi các gói được cấu trúc thành các gói con (như với gói sound trong ví dụ), bạn có thể sử dụng tính năng nhập tuyệt đối để tham chiếu đến các mô-đun con của các gói anh chị em. Ví dụ: nếu mô-đun sound.filters.vocoder cần sử dụng mô-đun echo trong gói sound.effects thì nó có thể sử dụng from sound.effects import echo.

Bạn cũng có thể viết các lệnh nhập tương đối, với dạng câu lệnh nhập from module import name. Việc nhập này sử dụng các dấu chấm ở đầu để biểu thị gói hiện tại và gói gốc liên quan đến quá trình nhập tương đối. Ví dụ: từ mô-đun surround, bạn có thể sử dụng:

từ . nhập tiếng vang
từ .. nhập định dạng
từ bộ cân bằng nhập ..filters

Lưu ý rằng việc nhập tương đối dựa trên tên gói của mô-đun hiện tại. Vì mô-đun chính không có gói nên các mô-đun được dùng làm mô-đun chính của ứng dụng Python phải luôn sử dụng tính năng nhập tuyệt đối.

6.4.3. Các gói trong nhiều thư mục

Các gói hỗ trợ thêm một thuộc tính đặc biệt, __path__. Chuỗi này được khởi tạo là một chuỗi sequence chứa tên của thư mục chứa __init__.py của gói trước khi mã trong tệp đó được thực thi. Biến này có thể được sửa đổi; làm như vậy sẽ ảnh hưởng đến việc tìm kiếm các mô-đun và gói con có trong gói trong tương lai.

Mặc dù tính năng này không thường xuyên cần thiết nhưng nó có thể được sử dụng để mở rộng tập hợp các mô-đun có trong một gói.

Chú thích cuối trang