Відносний імпорт мільярдний раз


716

Я був тут:

і багато URL-адрес, які я не копіював, деякі на SO, деякі на інших сайтах, коли я думав, що швидко знайду рішення.

Завжди повторюване питання таке: За допомогою Windows 7, 32-бітного Python 2.7.3, як вирішити це повідомлення "Спроба відносного імпорту в непакеті"? Я створив точну репліку пакета на pep-0328:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

Імпорт здійснювався з консолі.

Я робив функції з іменем спаму та яєць у своїх відповідних модулях. Природно, це не спрацювало. Відповідь, мабуть, у 4-й URL, яку я перерахував, але це все для мене випускники. На одну із URL-адрес, які я відвідав, була відповідь:

Відносний імпорт використовує атрибут імені модуля для визначення положення цього модуля в ієрархії пакетів. Якщо ім'я модуля не містить жодної інформації про пакет (наприклад, для нього встановлено значення "main"), відносний імпорт вирішується так, як якщо б модуль був модулем верхнього рівня, незалежно від того, де цей модуль фактично розташований у файловій системі.

Наведена вище відповідь виглядає багатообіцяючою, але це все для мене ієрогліфи. Отже, моє запитання, як змусити Python не повернути мені "Спроби відносного імпорту в непакеті"? має відповідь, яка передбачає -м, нібито.

Чи можете мені хтось сказати, чому Python дає це повідомлення про помилку, що це означає під "непакетним пакетом", чому і як ви визначаєте "пакунок", і точну відповідь, висловлену термінами, досить легко, щоб дитячий садок зрозумів .


5
Як ви намагаєтесь використовувати файли, які ви показуєте? Який код ви використовуєте?
BrenBarn

Див. Python.org/dev/peps/pep-0328 . Я використовував формат пакунка, який я описав у своєму дописі. У INIT файли .py порожні. moduleY.py має def spam(): pass, moduleA.py має def eggs(): pass. Я спробував виконати пару команд "з .something import something", але вони не спрацювали. Знову див. Pep-0328.

6
Дивіться мою відповідь. Ви все ще не до кінця уточнили, чим займаєтесь, але якщо ви намагаєтеся зробити це from .something import somethingв інтерактивному перекладачі, це не вийде. Відносний імпорт може використовуватися лише в межах модулів, а не в інтерактивному режимі.
BrenBarn

105
Сам факт, що у "мільярдів" людей - нормально 83,136 цього коментаря - виникає достатньо труднощів із імпортом для пошуку цього питання; можна лише зробити висновок, що імпорт пітона є контрінтуїтивним для багатьох, якщо не більшості програмістів. Гвідо, можливо, ти повинен це прийняти і попросити комітет перепроектувати механізм імпорту. Як мінімум, цей синтаксис повинен працювати, якщо x.py і z.py знаходяться в одному каталозі. А саме, якщо x.py має вислів, "from .z import MyZebraClass" x повинен імпортувати z EVEN, якщо він працює як основний ! Чому це так важко?
Стів Л

4
Прочитавши більшу частину цієї теми, хоча це не є відповіддю на запитання, "просто використовувати абсолютний імпорт", здається, рішення ...
CodeJockey

Відповіді:


1042

Сценарій проти модуля

Ось пояснення. Коротка версія полягає в тому, що існує велика різниця між безпосередньо запущеним файлом 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__. Таким чином, ви не можете здійснювати відносний імпорт безпосередньо з інтерактивного сеансу . Відносний імпорт використовується лише для файлів модулів.

Два рішення:

  1. Якщо ви дійсно хочете працювати moduleXбезпосередньо, але ви все ще хочете, щоб він вважався частиною пакету, ви можете це зробити python -m package.subpackage1.moduleX. -mКаже Python , щоб завантажити його як модуль, а не як сценарій верхнього рівня.

  2. Або, можливо, ви насправді не хочете запускатись 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.)


62
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.Це принципово тривожно. Що так важко переглядати поточний каталог? Python повинен бути здатний на це. Це виправлено у версії 3x?

7
@Stopforgettingmyaccounts ...: PEP 366 показує, як це працює. Всередині файлу ви можете зробити щось __package__ = 'package.subpackage1'подібне. Тоді тільки цей файл буде завжди розглядатися як частина цього пакету , навіть якщо працювати безпосередньо. Якщо у вас є інші питання про __package__вас, можливо, ви захочете задати окреме питання, оскільки ми вирішуємо питання вашого оригінального питання тут.
BrenBarn

108
Це має бути відповіддю на всі питання щодо імпорту Python щодо імпорту. Це має бути навіть у документах.
edsioufi

10
Див. Python.org/dev/peps/pep-0366 - "Зверніть увагу, що цього котла достатньо лише у тому випадку, якщо пакет верхнього рівня вже доступний через sys.path. Для прямого виконання потрібен додатковий код, який маніпулює sys.path. працювати, без того, щоб пакет вищого рівня вже не був важливим. " - це для мене найбільше заважає, оскільки цей "додатковий код" насправді досить довгий і його не можна зберігати в іншому місці пакета, щоб легко запустити.
Майкл Скотт Катберт

14
Ця відповідь наразі вимкнена на кілька важливих деталей щодо __name__та sys.path. Зокрема, з python -m pkg.mod, __name__встановлено __main__, а не pkg.mod; відносний імпорт вирішується за допомогою, __package__а не __name__в цьому випадку. Також Python додає каталог під sys.pathчас запуску python path/to/script.py, а не поточний каталог ; він додає поточний каталог до sys.pathзапуску більшості інших способів, у тому числі python -m pkg.mod.
user2357112 підтримує Моніку

42

Це справді проблема в python. Зародження плутанини полягає в тому, що люди помилково сприймають відносний імпорт як відносний шлях, якого немає.

Наприклад, коли ви пишете у faa.py :

from .. import foo

Це має значення лише в тому випадку, якщо faa.py був ідентифікований і завантажений python під час виконання, як частина пакету. У цьому випадку ім'я модуля для faa.py буде, наприклад, some_packagename.faa . Якщо файл був завантажений тільки тому, що він знаходиться в поточному каталозі, під час запуску python, його ім'я не посилатиметься на будь-який пакет і, зрештою, відносний імпорт не вдасться.

Просте рішення для посилання модулів у поточному каталозі полягає у використанні цього:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

5
Правильне рішення полягає from __future__ import absolute_importв тому, щоб змусити користувача правильно використовувати ваш код ... так що ви завжди можете це зробитиfrom . import foo
Giacomo Alzetta

@Giacomo: абсолютно правильна відповідь на мою проблему. Дякую!
Фабіо

8

Ось загальний рецепт, модифікований як приклад, який я зараз використовую для роботи з бібліотеками Python, написаними як пакунки, що містять взаємозалежні файли, де я хочу мати змогу перевірити їх частину. Давайте назвемо це lib.fooі скажемо, що йому потрібен доступ до lib.fileAфункцій f1і f2та lib.fileBкласу Class3.

Я включив кілька printдзвінків, щоб допомогти проілюструвати, як це працює. На практиці ви хочете їх видалити (а може бути і from __future__ import print_functionлінію).

Цей конкретний приклад занадто простий, щоб показати, коли нам дійсно потрібно вставити запис sys.path. (Дивіться відповідь Ларса на випадок, коли нам це потрібно, коли у нас є два або більше рівнів каталогів пакетів, а потім ми використовуємо os.path.dirname(os.path.dirname(__file__))— але і тут насправді не шкодить .) Також це досить безпечно, щоб це зробити і без if _i in sys.pathтест. Однак якщо кожен імпортований файл вставляє один і той же шлях - наприклад, якщо обидва fileAі fileBхочуть імпортувати утиліти з пакета - це sys.pathбагато разів переповнюється тим самим шляхом, тож приємно мати на if _i not in sys.pathпанелі котла.

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

Ідея тут така (і зауважте, що всі вони однаково функціонують у python2.7 та python 3.x):

  1. Якщо запускати як import libабо from lib import fooяк звичайний пакет імпорту з звичайного коду, __packageє libі __name__є lib.foo. Ми беремо перший шлях коду, імпортуючи з .fileAі т.д.

  2. Якщо запустити як python lib/foo.py, __package__буде None і __name__буде __main__.

    Ми беремо другий шлях коду. libКаталог вже буде , sys.pathтак що немає необхідності додавати його. Ми імпортуємо з fileAтощо.

  3. Якщо запустити всередині libкаталогу як python foo.py, поведінка така ж, як у випадку 2.

  4. Якщо запускати в межах lib каталозі як python -m foo, поведінка схожа на випадки 2 і 3. Однак шлях до libкаталогу не знаходиться sys.path, тому ми додаємо його до імпорту. Це ж стосується, якщо ми запускаємо Python і тоді import foo.

    (З тих пір . знаходиться в sys.pathнас, нам дійсно не потрібно додавати сюди абсолютну версію шляху. Тут дещо змінюється більш глибока структура вкладання пакету, де ми хочемо це зробити from ..otherlib.fileC import .... Якщо ви цього не робите, ви можете повністю пропустити sys.pathманіпуляції.)

Примітки

Ще є примха. Якщо ви керуєте цією справою зовні:

$ python2 lib.foo

або:

$ python3 lib.foo

поведінка залежить від змісту lib/__init__.py. Якщо це існує і порожнє , все добре:

Package named 'lib'; __name__ is '__main__'

Але якщо lib/__init__.py сам імпортує routineтак, що він може експортувати routine.nameбезпосередньо якlib.name , ви отримуєте:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

Тобто модуль імпортується двічі, один раз через пакет і потім знову __main__так, щоб він запускав вашmain код. Python 3.6 і пізніше попереджають про це:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

Попередження є новим, але попередив, про поведінку немає. Це частина того, що деякі називають пасткою подвійного імпорту . (Докладні відомості див випуску 27487. ) Нік Коглан говорить:

Ця наступна пастка існує у всіх поточних версіях Python, включаючи 3.3, і може бути підсумована у наступному загальному керівництві: "Ніколи не додайте каталог пакунків або будь-який каталог всередині пакета безпосередньо до шляху Python".

Зауважте, що, хоча ми порушуємо це правило тут, ми робимо це лише тоді, коли файл, що завантажується, не є завантажується як частина пакету, і наша модифікація розроблена спеціально для того, щоб ми могли отримати доступ до інших файлів у цьому пакеті. (І, як я зазначив, ми, мабуть, не повинні цього робити взагалі для однорівневих пакетів.) Якщо ми хотіли бути надзвичайно чистими, ми могли б переписати це як, наприклад:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

Тобто ми змінюємо sys.pathдосить довго, щоб досягти нашого імпорту, а потім повертаємо його таким, яким він був (видаляючи одну копію, _iякщо і лише тоді, коли ми додавали одну копію _i).


7

Отож, вирішивши про це разом з багатьма іншими, я натрапив на замітку, опубліковану Доріаном Б у цій статті, яка вирішила конкретну проблему, з якою я мав би розробляти модулі та класи для використання з веб-сервісом, але я також хочу бути в змозі перевірити їх, як я кодую, використовуючи засоби налагодження в PyCharm. Для запуску тестів в автономному класі я б включив наступне в кінці файлу свого класу:

if __name__ == '__main__':
   # run test code here...

але якщо я хотів імпортувати інші класи або модулі в ту саму папку, я повинен був би змінити всі свої імпортні заяви з відносної нотації на локальні посилання (тобто видалити крапку (.)) Але, прочитавши пропозицію Доріана, я спробував його ' однолінійний 'і це спрацювало! Тепер я можу протестувати в PyCharm і залишити свій тестовий код на місці, коли я використовую клас в іншому тестованому класі або коли я використовую його у своєму веб-сервісі!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

Оператор if перевіряє, чи працює цей модуль як основний, чи він використовується в іншому модулі, який тестується як основний . Можливо, це очевидно, але я пропоную цю замітку тут на випадок, якщо хтось інший, розчарований питаннями відносного імпорту, може скористатися цим.


1
Це насправді це вирішує. Але це справді противно. Чому це не поведінка за замовчуванням ?!
lo tolmencre

4

Ось одне рішення, яке я б не рекомендував, але може бути корисним у деяких ситуаціях, коли модулі просто не генеруються:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()

2

У мене була подібна проблема, коли я не хотів змінювати шлях пошуку модуля Python і мені потрібно було завантажувати модуль відносно сценарію (незважаючи на те, що "сценарії не можуть імпортувати відносно всіх" як це добре пояснив BrenBarn вище).

Тому я використав наступний хак. На жаль, він покладається на impмодуль, який застарів з моменту відмови версії 3.4 на користь importlib. (Чи можливо це importlibтеж за допомогою? Не знаю.) Все-таки хак працює на даний момент.

Приклад доступу до членів програми moduleXв subpackage1скрипті, що знаходиться в subpackage2папці:

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

Здається, чистішим підходом є зміна sys.path, що використовується для завантаження модулів, як згадував Federico.

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *

Це виглядає краще ... занадто погано, все-таки потрібно, щоб ви вставили ім'я батьківського каталогу у файл ... можливо, це можна покращити за допомогою importlib. Можливо, importlib можна навіть привласнити мавпами, щоб зробити відносний імпорт "просто робочим" для простих випадків використання. Я візьму тріщину на це.
Ендрю Вагнер

Я використовую python 2.7.14, хоча. Чи щось подібне досі працює?
користувач3474042

Я просто перевірив обидва підходи на python 2.7.10, і вони добре працювали на мене. Якщо насправді, у вас немає проблеми із застарілим модулем імпульсів у 2.7, тож ще краще.
Ларс

2

__name__ змінюється залежно від того, чи працює відповідний код у глобальній просторі імен або як частина імпортованого модуля.

Якщо код не працює в глобальному просторі, __name__буде ім'ям модуля. Якщо він працює в глобальній просторі імен - наприклад, якщо ви введете його в консоль, або запускаєте модуль як сценарій, використовуючи python.exe yourscriptnamehere.pyпотім, __name__стає"__main__" .

Ви побачите багато пітонного коду if __name__ == '__main__' якого використовується для перевірки того, чи запускається код із глобальної простори імен - це дозволяє вам мати модуль, що подвоюється як сценарій.

Ви намагалися зробити цей імпорт з консолі?


Ах, так ви згадаєте -м. Це змушує ваш модуль виконуватись як сценарій - якщо ви вставите в нього if __name__ == '__main__', ви повинні побачити, що він '__main__' через -m. Спробуйте просто імпортувати свій модуль в інший модуль, щоб він не був на найвищому рівні ... це повинно дозволяти вам робити відносний імпорт
theodox

Я намагався зробити цей імпорт з консолі, при цьому активний файл був правильним модулем.

@Stopforgettingmyaccounts ...: Що означає "активний файл"?
BrenBarn

Я використовую Pyscripter. Я працював у moduleX.py, коли запускав цей імпорт: від .moduleY імпорту спаму та з. імпортний модульY.

Не імпортувати .moduleY з подальшим модулемY.spam ()?
Феодокс

2

@ Відповідь BrenBarn говорить про все, але якщо ти такий, як я, це може зайняти деякий час. Ось мій випадок і як відповідь @ BrenBarn стосується цього, можливо, це вам допоможе.

Справа

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

Використовуючи наш знайомий приклад та додайте до нього, що moduleX.py має відносний імпорт до ..moduleA. Зважаючи на те, що я спробував написати тестовий скрипт у каталог subpackage1, який імпортував moduleX, але потім отримав жахливу помилку, описану ОП.

Рішення

Перемістіть тестовий скрипт на той самий рівень, що й пакунок та імпорт пакета.subpackage1.moduleX

Пояснення

Як було пояснено, відносний імпорт виробляється відносно поточної назви. Коли мій тестовий скрипт імпортує moduleX з того самого каталогу, то ім'я модуля всередині moduleX є moduleX. Коли він стикається з відносним імпортом, інтерпретатор не може створити резервну копію ієрархії пакетів, оскільки вона вже знаходиться вгорі

Коли я імпортую moduleX зверху, то ім'я у moduleX є package.subpackage1.moduleX, і відносний імпорт можна знайти


Сподіваючись, ти зможеш мене на цьому направити. У наступному посиланні, якщо ви переходите до справи 3, він говорить, що рішення 1 неможливо. Будь ласка, ви можете перевірити це та повідомити мені. Це мені дуже допоможе. chrisyeh96.github.io/2017/08/08/…
змінна

@variable є помилка друку у посиланні, і мені заборонено редагувати. Подивився на випадок 3 і не стежив за тим, що саме ти отримуєш. Коли я спробував цей приклад в python 2, жодних проблем не було, що змушує мене думати, що я щось пропустив. Можливо, вам слід поставити нове запитання, але потрібно навести більш чіткий приклад. Випадок 4 торкається того, про що я говорю у своїй відповіді тут: ви не можете перейти до каталогу для відносного імпорту, НЕ БЕЗПЕКИ, інтерпретатор починається у батьківському каталозі
Бред Дре

Дякую, я маю на увазі python 3 і тут питання stackoverflow.com/questions/58577767/…
змінна

1

Відносний імпорт використовує атрибут імені модуля для визначення положення цього модуля в ієрархії пакетів. Якщо ім'я модуля не містить жодної інформації про пакет (наприклад, для нього встановлено значення "main"), відносний імпорт вирішується так, як якщо б модуль був модулем верхнього рівня, незалежно від того, де цей модуль фактично розташований у файловій системі.

Написав трохи Python-пакету в PyPi, який може допомогти глядачам цього питання. Пакет діє як спосіб вирішення, якщо хочеться запускати файли python, що містять імпорт, що містить пакети верхнього рівня зсередини пакету / проекту, не знаходячись безпосередньо в каталозі файлу імпорту. https://pypi.org/project/import-anywhere/


-2

Щоб Python не повернувся до мене "Спроба відносного імпорту в непакеті". пакет /

init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py

Ця помилка виникає лише в тому випадку, якщо ви застосовуєте відносний імпорт до батьківського файлу. Наприклад, батьківський файл вже повертає основний після коду "print ( ім'я кодуєте )" в moduleA.py. Тому ЦЕ файл вже є основнийвін не може повернути жоден батьківський пакет далі. відносний імпорт необхідний у файлах пакетів subpackage1 та subpackage2, ви можете використовувати ".." для посилання на батьківський каталог або модуль. Але батьківський, якщо вже пакет верхнього рівня, він не може пройти далі над цим батьківським каталогом (пакетом). Такі файли, де ви застосовуєте відносний імпорт для батьків, можуть працювати лише із застосуванням абсолютного імпорту. Якщо ви будете використовувати АБСОЛЮТНИЙ ІМПОРТ ДЛЯ РОБОТИХ ПАКЕТІВ, НЕ ПОМИЛКУВАТЬСЯ, оскільки пітон знає, хто знаходиться на найвищому рівні пакету, навіть якщо ваш файл знаходиться в підпакеті через концепцію PYTHON PATH, яка визначає верхній рівень проекту

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.