Kịch bản so với mô-đun
Đây là một lời giải thích. Phiên bản ngắn là có một sự khác biệt lớn giữa trực tiếp chạy tệp Python và nhập tệp đó từ nơi khác. Chỉ cần biết tập tin nằm trong thư mục nào không xác định gói Python nghĩ là gì. Ngoài ra, điều đó còn phụ thuộc vào cách bạn tải tệp vào Python (bằng cách chạy hoặc bằng cách nhập).
Có hai cách để tải tệp Python: dưới dạng tập lệnh cấp cao nhất hoặc dưới dạng mô-đun. Ví dụ, một tệp được tải dưới dạng tập lệnh cấp cao nhất nếu bạn thực thi trực tiếp bằng cách nhập python myfile.py
vào dòng lệnh. Nó được tải dưới dạng một mô-đun nếu bạn thực hiện python -m myfile
hoặc nếu nó được tải khi gặp một import
câu lệnh bên trong một số tệp khác. Mỗi lần chỉ có thể có một kịch bản cấp cao nhất; tập lệnh cấp cao nhất là tệp Python bạn đã chạy để bắt đầu mọi thứ.
Đặt tên
Khi một tập tin được tải, nó được đặt tên (được lưu trong __name__
thuộc tính của nó ). Nếu nó được tải dưới dạng tập lệnh cấp cao nhất, tên của nó là __main__
. Nếu nó được tải dưới dạng một mô-đun, tên của nó là tên tệp, trước tên của bất kỳ gói / gói con nào mà nó là một phần, được phân tách bằng dấu chấm.
Vì vậy, ví dụ trong ví dụ của bạn:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
nếu bạn đã nhập moduleX
(lưu ý: đã nhập , không được thực thi trực tiếp), tên của nó sẽ là package.subpackage1.moduleX
. Nếu bạn nhập moduleA
, tên của nó sẽ là package.moduleA
. Tuy nhiên, nếu bạn trực tiếp chạy moduleX
từ dòng lệnh, thay vào đó tên của nó sẽ là __main__
, và nếu bạn trực tiếp chạy moduleA
từ dòng lệnh, tên của nó sẽ là __main__
. Khi một mô-đun được chạy dưới dạng tập lệnh cấp cao nhất, nó sẽ mất tên bình thường và thay vào đó là tên của nó __main__
.
Truy cập một mô-đun KHÔNG thông qua gói chứa nó
Có một nếp nhăn bổ sung: tên của mô-đun phụ thuộc vào việc nó được nhập "trực tiếp" từ thư mục mà nó nằm trong hoặc được nhập qua gói. Điều này chỉ tạo ra sự khác biệt nếu bạn chạy Python trong một thư mục và cố gắng nhập một tệp trong cùng thư mục đó (hoặc thư mục con của nó). Chẳng hạn, nếu bạn khởi động trình thông dịch Python trong thư mục package/subpackage1
và sau đó thực hiện import moduleX
, tên của moduleX
sẽ chỉ là moduleX
, và không package.subpackage1.moduleX
. Điều này là do Python thêm thư mục hiện tại vào đường dẫn tìm kiếm của nó khi khởi động; nếu nó tìm thấy mô-đun được nhập trong thư mục hiện tại, nó sẽ không biết rằng thư mục đó là một phần của gói và thông tin gói sẽ không trở thành một phần của tên mô-đun.
Một trường hợp đặc biệt là nếu bạn chạy trình thông dịch một cách tương tác (ví dụ: chỉ cần gõ python
và bắt đầu nhập mã Python một cách nhanh chóng). Trong trường hợp này tên của phiên tương tác đó là __main__
.
Bây giờ đây là điều cốt yếu cho thông báo lỗi của bạn: nếu tên của mô-đun không có dấu chấm, thì nó không được coi là một phần của gói . Nó không quan trọng nơi tập tin thực sự nằm trên đĩa. Tất cả vấn đề là tên của nó là gì và tên của nó phụ thuộc vào cách bạn tải nó.
Bây giờ hãy nhìn vào trích dẫn bạn đưa vào câu hỏi của bạn:
Nhập khẩu tương đối sử dụng thuộc tính tên của mô-đun để xác định vị trí của mô-đun đó trong phân cấp gói. Nếu tên của mô-đun không chứa bất kỳ thông tin gói nào (ví dụ: nó được đặt thành 'chính') thì việc nhập tương đối được giải quyết như thể mô-đun là mô-đun cấp cao nhất, bất kể mô-đun thực sự nằm ở đâu trên hệ thống tệp.
Nhập khẩu tương đối ...
Nhập khẩu tương đối sử dụng tên của mô-đun để xác định vị trí của gói trong gói. Khi bạn sử dụng nhập tương đối from .. import foo
, các dấu chấm biểu thị để tăng một số cấp trong phân cấp gói. Ví dụ, nếu tên mô-đun hiện tại của bạn là package.subpackage1.moduleX
, thì ..moduleA
có nghĩa là package.moduleA
. Để from .. import
làm việc, tên của mô-đun phải có ít nhất nhiều dấu chấm như trong import
câu lệnh.
... chỉ tương đối trong một gói
Tuy nhiên, nếu tên mô-đun của bạn là __main__
, nó không được coi là trong một gói. Tên của nó không có dấu chấm, và do đó bạn không thể sử dụng các from .. import
câu lệnh bên trong nó. Nếu bạn cố gắng làm như vậy, bạn sẽ gặp lỗi "nhập tương đối trong gói không".
Tập lệnh không thể nhập tương đối
Những gì bạn có thể đã làm là bạn đã cố chạy moduleX
hoặc tương tự từ dòng lệnh. Khi bạn thực hiện việc này, tên của nó được đặt thành __main__
, điều đó có nghĩa là nhập khẩu tương đối trong đó sẽ không thành công, vì tên của nó không tiết lộ rằng nó nằm trong một gói. Lưu ý rằng điều này cũng sẽ xảy ra nếu bạn chạy Python từ cùng thư mục có mô-đun, sau đó thử nhập mô-đun đó, vì như mô tả ở trên, Python sẽ tìm thấy mô-đun trong thư mục hiện tại "quá sớm" mà không nhận ra đó là mô-đun một phần của gói
Cũng nên nhớ rằng khi bạn chạy trình thông dịch tương tác, "tên" của phiên tương tác đó luôn luôn __main__
. Do đó, bạn không thể thực hiện nhập khẩu tương đối trực tiếp từ một phiên tương tác . Nhập khẩu tương đối chỉ được sử dụng trong các tập tin mô-đun.
Hai giải pháp:
Nếu bạn thực sự muốn chạy moduleX
trực tiếp, nhưng bạn vẫn muốn nó được coi là một phần của gói, bạn có thể làm python -m package.subpackage1.moduleX
. Việc -m
bảo Python tải nó dưới dạng một mô-đun, không phải là tập lệnh cấp cao nhất.
Hoặc có lẽ bạn không thực sự muốn chạy moduleX
, bạn chỉ muốn chạy một số tập lệnh khác, giả sử myfile.py
, sử dụng các chức năng bên trong moduleX
. Nếu đó là trường hợp, đặt myfile.py
một nơi khác - không phải trong package
thư mục - và chạy nó. Nếu bên trong myfile.py
bạn làm những việc như thế from package.moduleA import spam
, nó sẽ hoạt động tốt.
Ghi chú
Đối với một trong những giải pháp này, thư mục gói ( package
trong ví dụ của bạn) phải có thể truy cập được từ đường dẫn tìm kiếm mô-đun Python ( sys.path
). Nếu không, bạn sẽ không thể sử dụng bất cứ thứ gì trong gói một cách đáng tin cậy cả.
Kể từ Python 2.6, "tên" của mô-đun cho các mục đích phân giải gói được xác định không chỉ bởi các __name__
thuộc tính của nó mà còn bởi __package__
thuộc tính. Đó là lý do tại sao tôi tránh sử dụng biểu tượng rõ ràng __name__
để chỉ "tên" của mô-đun. Vì Python 2.6, "tên" của mô-đun có hiệu quả __package__ + '.' + __name__
hoặc chỉ là __name__
nếu __package__
có None
.)