Сценарій проти модуля
Ось пояснення. Коротка версія полягає в тому, що існує велика різниця між безпосередньо запущеним файлом Python та імпортом цього файла з іншого місця. Просто знання того, в якому каталозі знаходиться файл, не визначає, в якому пакеті Python вважає його. Це, крім того, залежить від того, яким чином ви завантажуєте файл у Python (за допомогою запуску чи імпорту).
Існує два способи завантаження файлу Python: як сценарій верхнього рівня, або як модуль. Файл завантажується як сценарій верхнього рівня, якщо ви його виконуєте безпосередньо, наприклад, ввівши python myfile.py
командний рядок. Він завантажується як модуль, якщо ви це робите python -m myfile
, або якщо він завантажений, коли import
у будь-якому іншому файлі виникає оператор. Одночасно може бути лише один сценарій верхнього рівня; сценарій верхнього рівня - це файл Python, який ви запустили, щоб почати роботу.
Іменування
Коли файл завантажується, йому надається ім'я (яке зберігається в його __name__
атрибуті). Якщо він був завантажений як сценарій верхнього рівня, його ім'я __main__
. Якщо він був завантажений як модуль, його ім'я - це ім'я файлу, перед яким назви будь-яких пакетів / підпакетів, частина яких є частиною, розділених крапками.
Так, наприклад, у вашому прикладі:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
якщо ви імпортували moduleX
(зауважте: імпорт , не виконаний безпосередньо), його назва буде package.subpackage1.moduleX
. Якщо ви імпортували moduleA
, його назва буде package.moduleA
. Однак якщо ви безпосередньо запустите moduleX
з командного рядка, замість цього буде його ім'я __main__
, а якщо ви безпосередньо запустите moduleA
з командного рядка, його ім'я буде __main__
. Коли модуль запускається як сценарій верхнього рівня, він втрачає звичайне ім'я, а його ім'я замість цього __main__
.
Доступ до модуля НЕ через пакет, що містить його
Існує додаткова зморшка: назва модуля залежить від того, чи був він імпортований "безпосередньо" з каталогу, в якому він знаходиться, чи імпортований через пакет. Це має значення лише в тому випадку, якщо ви запускаєте Python у каталозі та намагаєтесь імпортувати файл у той самий каталог (або його підкаталог). Наприклад, якщо ви запустили інтерпретатор Python в каталозі, package/subpackage1
а потім зробите import moduleX
, ім'я moduleX
буде просто moduleX
, а ні package.subpackage1.moduleX
. Це тому, що Python додає поточний каталог до свого шляху пошуку при запуску; якщо він знайде модуль, який підлягає імпортуванню, у поточному каталозі, він не буде знати, що цей каталог є частиною пакету, і інформація про пакет не стане частиною імені модуля.
Особливий випадок, якщо ви запускаєте інтерпретатор інтерактивно (наприклад, просто введіть python
і почніть вводити код Python на льоту). У цьому випадку назва цього інтерактивного сеансу __main__
.
Тепер ось найважливіша річ для вашого повідомлення про помилку: якщо в імені модуля немає крапок, воно не вважається частиною пакету . Не має значення, де файл насправді знаходиться на диску. Важливо лише те, як його звати, а його назва залежить від того, як ви його завантажили.
А тепер подивіться на цитату, яку ви включили у своє запитання:
Відносний імпорт використовує атрибут імені модуля для визначення положення цього модуля в ієрархії пакетів. Якщо ім'я модуля не містить жодної інформації про пакет (наприклад, для нього встановлено значення "main"), відносний імпорт вирішується так, як якщо б модуль був модулем верхнього рівня, незалежно від того, де цей модуль фактично розташований у файловій системі.
Відносний імпорт ...
Відносний імпорт використовувати модуль ім'я , щоб визначити , де він знаходиться в пакеті. Коли ви використовуєте подібний імпорт, наприклад from .. import foo
, точки вказують на збільшення кількості рівнів в ієрархії пакетів. Наприклад, якщо ім'я вашого поточного модуля package.subpackage1.moduleX
, то ..moduleA
це означатиме package.moduleA
. Щоб from .. import
робота працювала, ім'я модуля повинно мати принаймні стільки крапок, скільки їх у import
виписці.
... відносні лише в упаковці
Однак, якщо ім'я вашого модуля __main__
, воно не вважається в пакеті. У його назви немає крапок, і тому ви не можете використовувати from .. import
оператори всередині нього. Якщо ви спробуєте це зробити, ви отримаєте помилку "відносний імпорт у непакеті".
Сценарії не можуть імпортувати відносні
Що ви, напевно, робили - це ви намагалися запустити moduleX
або подібне з командного рядка. Коли ви зробили це, його ім'я було встановлено на __main__
, а це означає, що відносний імпорт всередині нього буде невдалим, оскільки його назва не показує, що він знаходиться в пакеті. Зауважте, що це також станеться, якщо запустити Python з того самого каталогу, де є модуль, а потім спробувати імпортувати цей модуль, оскільки, як описано вище, Python знайде модуль у поточному каталозі "занадто рано", не усвідомлюючи це частина пакету.
Також пам’ятайте, що при запуску інтерактивного інтерпретатора завжди є "ім'я" цього інтерактивного сеансу __main__
. Таким чином, ви не можете здійснювати відносний імпорт безпосередньо з інтерактивного сеансу . Відносний імпорт використовується лише для файлів модулів.
Два рішення:
Якщо ви дійсно хочете працювати moduleX
безпосередньо, але ви все ще хочете, щоб він вважався частиною пакету, ви можете це зробити python -m package.subpackage1.moduleX
. -m
Каже Python , щоб завантажити його як модуль, а не як сценарій верхнього рівня.
Або, можливо, ви насправді не хочете запускатись moduleX
, ви просто хочете запустити якийсь інший сценарій, скажімо myfile.py
, який використовує функції всередині moduleX
. Якщо це так, поставте myfile.py
де-небудь ще - не всередині package
каталогу - і запустіть його. Якщо всередині myfile.py
ви робите подібні речі from package.moduleA import spam
, це буде добре.
Примітки
Для будь-якого з цих рішень каталог каталогу ( package
у вашому прикладі) повинен бути доступний через шлях пошуку модуля Python ( sys.path
). Якщо це не так, ви взагалі нічого не зможете надійно використовувати в упаковці.
Оскільки Python 2.6, "ім'я" модуля для вирішення пакету визначається не лише його __name__
атрибутами, але й __package__
атрибутом. Ось чому я уникаю використання явного символу __name__
для позначення "імені" модуля. Оскільки Python 2.6 "ім'я" модуля ефективно __package__ + '.' + __name__
, або просто, __name__
якщо __package__
є None
.)