Я хочу импортировать функцию из другого файла в том же каталоге.
Иногда это работает для меня, from .mymodule import myfunction
но иногда я получаю:
SystemError: Parent module '' not loaded, cannot perform relative import
Иногда это работает с from mymodule import myfunction
, но иногда я также получаю:
SystemError: Parent module '' not loaded, cannot perform relative import
Я не понимаю логику здесь, и я не мог найти никакого объяснения. Это выглядит совершенно случайно.
Может ли кто-нибудь объяснить мне, что за всем этим стоит логика?
python
python-3.x
python-import
Джон Смит Необязательно
источник
источник
Ответы:
Это довольно распространено, чтобы иметь такой макет ...
... с таким,
mymodule.py
как это ......
myothermodule.py
как это ...... и тому
main.py
подобное ...... который работает нормально, когда вы запускаете
main.py
илиmypackage/mymodule.py
, но не удаетсяmypackage/myothermodule.py
, из-за относительного импорта ...То, как вы должны запустить это ...
... но это несколько многословно, и не очень хорошо сочетается с линией Шебанга, как
#!/usr/bin/env python3
.Простейшим решением для этого случая, если предположить, что имя
mymodule
является глобально уникальным, было бы избежать использования относительного импорта и просто использовать ...... хотя, если он не уникален или структура вашего пакета более сложная, вам нужно включить каталог, в котором находится каталог вашего пакета
PYTHONPATH
, и сделать это следующим образом ...... или если вы хотите, чтобы он работал "из коробки", вы можете сначала добавить
PYTHONPATH
код в код с помощью этого ...Это своего рода боль, но есть ключ к пониманию того, почему в электронном письме, написанном неким Гвидо ван Россумом ...
Является ли выполнение сценариев внутри пакета антипаттерном или нет, это субъективно, но лично я нахожу это действительно полезным в моем пакете, который содержит несколько пользовательских виджетов wxPython, поэтому я могу запустить сценарий для любого из исходных файлов, чтобы отобразить
wx.Frame
только содержащий этот виджет для тестирования.источник
os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
если бы вы были уверены, что ваш модуль всегда имеет свойство, котороеfile
вы также можете использоватьos.path.realpath(os.path.dirname(__file__))
.sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
...which I've always seen as an antipattern.
Я не понимаю, как это анти-паттерн ... Кажется, было бы очень удобно просто сделать относительный импорт интуитивно понятным. Я просто хочу иметь возможность импортировать вещи, которые я знаю, находятся в одном каталоге. Интересно, каково было его рассуждениеобъяснение
От PEP 328
В какой-то момент PEP 338 вступил в конфликт с PEP 328 :
и для решения этой проблемы PEP 366 представил переменную верхнего уровня
__package__
:(акцент мой)
Если
__name__
есть'__main__'
,__name__.rpartition('.')[0]
возвращает пустую строку. Вот почему в описании ошибки есть пустой строковый литерал:Соответствующая часть в CPython в
PyImport_ImportModuleLevelObject
функции :CPython вызывает это исключение, если не удалось найти
package
(имя пакета) вinterp->modules
(доступно какsys.modules
). Посколькуsys.modules
это «словарь, который отображает имена модулей на модули, которые уже были загружены» , теперь ясно, что родительский модуль должен быть явно импортирован абсолютно перед выполнением относительного импорта .Примечание: патч из выпуска 18018 добавил еще один
if
блок , который будет выполнен перед кодом выше:Если
package
(как указано выше) пустая строка, сообщение об ошибке будетТем не менее, вы увидите это только в Python 3.6 или новее.
Решение № 1: Запустите ваш скрипт, используя -m
Рассмотрим каталог (который является пакетом Python ):
Все файлы в пакете начинаются с одинаковых двух строк кода:
Я включил эти две строки только для того, чтобы сделать порядок операций очевидным. Мы можем полностью их игнорировать, поскольку они не влияют на исполнение.
__init__.py и module.py содержат только эти две строки (т.е. они фактически пусты).
standalone.py дополнительно пытается импортировать module.py через относительный импорт:
Мы прекрасно понимаем, что не
/path/to/python/interpreter package/standalone.py
получится. Однако мы можем запустить модуль с параметром-m
командной строки, который будет «искатьsys.path
указанный модуль и выполнять его содержимое как__main__
модуль» :-m
выполняет все операции импорта для вас и автоматически устанавливает__package__
, но вы можете сделать это самостоятельно вРешение № 2: Установите __package__ вручную
Пожалуйста, рассматривайте это как доказательство концепции, а не фактическое решение. Он не очень подходит для использования в реальном коде.
В PEP 366 есть обходной путь для этой проблемы, однако он неполон, поскольку
__package__
одной настройки недостаточно. Вам нужно будет импортировать как минимум N предыдущих пакетов в иерархии модулей, где N - это количество родительских каталогов (относительно каталога скрипта), в которых будет выполняться поиск импортируемого модуля.Таким образом,
Добавьте родительский каталог N-го предшественника текущего модуля в
sys.path
Удалить каталог текущего файла из
sys.path
Импортировать родительский модуль текущего модуля, используя его полное имя
Установите
__package__
полное имя из 2Выполните относительный импорт
Я позаимствую файлы из Решения № 1 и добавлю еще несколько подпакетов:
На этот раз standalone.py импортирует module.py из пакета , используя следующий относительный импорт
Нам нужно будет поставить перед этой строкой стандартный код, чтобы она заработала.
Это позволяет нам выполнять standalone.py по имени файла:
Более общее решение, заключенное в функцию, можно найти здесь . Пример использования:
Решение № 3: Используйте абсолютный импорт и установочные инструменты
Шаги -
Заменить явный относительный импорт эквивалентным абсолютным импортом
Установите,
package
чтобы сделать его импортируемымНапример, структура каталога может быть следующей
где setup.py является
Остальные файлы были заимствованы из Решения № 1 .
Установка позволит вам импортировать пакет независимо от вашего рабочего каталога (при условии, что проблем с именами не будет).
Мы можем изменить standalone.py, чтобы использовать это преимущество (шаг 1):
Измените ваш рабочий каталог на
project
и запустите/path/to/python/interpreter setup.py install --user
(--user
устанавливает пакет в ваш каталог site-packages ) (шаг 2):Давайте проверим, что теперь можно запускать standalone.py как скрипт:
Примечание . Если вы решите пойти по этому пути, вам лучше использовать виртуальные среды для установки пакетов изолированно.
Решение № 4: Используйте абсолютный импорт и некоторый шаблонный код
Честно говоря, установка не обязательна - вы можете добавить шаблонный код в ваш скрипт, чтобы обеспечить абсолютный импорт.
Я собираюсь позаимствовать файлы из решения № 1 и изменить standalone.py :
Добавить родительский каталог пакета , чтобы
sys.path
прежде , чем пытаться что - либо импорт из пакета с использованием абсолютного импорта:Замените относительный импорт на абсолютный импорт:
standalone.py работает без проблем:
Я чувствую, что должен предупредить вас: старайтесь не делать этого, особенно если ваш проект имеет сложную структуру.
В качестве примечания, PEP 8 рекомендует использовать абсолютный импорт, но утверждает, что в некоторых сценариях допустим явный относительный импорт:
источник
__package__
вручную, если имя__main__
для решения проблемы?imp
модуля и установить его__package__
соответствующим образом, но результат явно является анти-паттерном.AttributeError: 'PosixPath' object has no attribute 'path'
.Поместите это в файл вашего пакета __init__.py :
Предполагая, что ваш пакет выглядит так:
Теперь используйте регулярный импорт в ваш пакет, например:
Это работает как в Python 2 и 3.
источник
__init__.py
будет в основном разрешать все относительные ошибки импорта.sys.path
потому что я обеспокоен тем, что это может повлиять на другой код. (Частично это потому, что я не знаю тонкостей того, как это работает.)Я столкнулся с этим вопросом. Обходной путь взлома - импорт через блок if / else, как показано ниже:
источник
except:
это плохо. используйтеexcept ImportError:
вместо этого!SystemError
здесь. (Py 3.4)if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
.Надеюсь, это будет полезно для кого-то там - я просмотрел полдюжины постов, посвященных стековому потоку, пытаясь выяснить относительный импорт, аналогичный тому, что был опубликован здесь выше. Я настроил все как предложено, но я все еще бил
ModuleNotFoundError: No module named 'my_module_name'
Так как я только разрабатывал локально и играл, я не создал / не запустил
setup.py
файл. Я также, очевидно, не установил свойPYTHONPATH
.Я понял, что, когда я запустил свой код, как это было, когда тесты были в том же каталоге, что и модуль, я не смог найти свой модуль:
Однако, когда я явно указал путь, вещи начали работать:
Таким образом, в случае, если кто-то попробовал несколько предложений, считает, что их код структурирован правильно, и все равно оказывается в подобной ситуации, как я, попробуйте выполнить одно из следующих действий, если вы не экспортируете текущий каталог в свой PYTHONPATH:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, создайтеsetup.py
файл с содержимым, подобным следующему, и запустите,python setup.py development
чтобы добавить пакеты к пути:источник
Мне нужно было запустить python3 из основного каталога проекта, чтобы он работал.
Например, если проект имеет следующую структуру:
Решение
Я бы запустил python3 внутри папки project_demo /, а затем выполнил бы
источник
Чтобы устранить эту проблему, я разработал решение с пакетом repackage , который работал для меня в течение некоторого времени. Это добавляет верхний каталог к пути lib:
Переупаковка может сделать относительный импорт, который работает в широком диапазоне случаев, используя интеллектуальную стратегию (проверка стека вызовов).
источник
если оба пакета находятся в вашем пути импорта (sys.path), а нужный вам модуль / класс находится в example / example.py, то для доступа к классу без относительного импорта попытайтесь:
источник
Я думаю, что лучшее решение - создать пакет для вашего модуля: вот больше информации о том, как это сделать.
Если у вас есть пакет, вам не нужно беспокоиться об относительном импорте, вы можете просто выполнить абсолютный импорт.
источник
У меня была похожая проблема: мне нужен был сервис Linux и плагин cgi, которые используют общие константы для взаимодействия. «Естественный» способ сделать это - поместить их в init .py пакета, но я не могу запустить плагин cgi с параметром -m.
Мое окончательное решение было похоже на Решение № 2 выше:
Недостатком является то, что вы должны ставить перед константами (или общими функциями) префикс pkg:
источник