Как импортировать из родственного каталога в python3?

У меня есть следующая файловая структура:

bot
├── LICENSE.md
├── README.md
├── bot.py # <-- file that is executed from command line
├── plugins
│   ├── __init__.py
│   ├── debug.py
│   └── parsemessages.py
├── helpers
│   ├── __init__.py
│   ├── parse.py
│   └── greetings.py
└── commands
    ├── __init__.py
    └── search.py

bot.py при выполнении из командной строки загрузит все, что находится в каталоге plugins.

Я хочу plugins/parsemessages.py импортировать parse из каталога helpers, поэтому я делаю это:

# parsemessages.py
from ..helpers import parse
parse.execute("string to be parsed")

Я запускаю python3 bot.py из командной строки.

Я получаю следующую ошибку:

File "/home/bot/plugins/parsemessages.py", line 2, in <module>
  from ..helpers import parse
ValueError: attempted relative import beyond top-level package

Поэтому я меняю две точки на одну:

# parsemessages.py
from .helpers import parse
parse.execute("string to be parsed")

... но я получаю другую ошибку:

File "/home/bot/plugins/parsemessages.py", line 2, in <module>
  from .helpers import parse
ImportError: No module named 'plugins.helpers'

Как я могу заставить этот импорт работать?

Стоит отметить, что я не пытаюсь сделать здесь пакет, это обычный скрипт. При этом я не хочу возиться с sys.path - я хочу, чтобы это было чисто в использовании.

Кроме того, я хочу, чтобы parse импортировалось как parse, поэтому для приведенного выше примера я должен печатать parse.execute(), а не execute().

Я нашел эта почта и эта почта, но они начинаются с файла, который находится довольно глубоко в файловой структуре (мой находится прямо вверху). Я также нашел эта почта, но, похоже, речь идет о пакете, а не просто об обычном .py.

Какое решение здесь?

from . import foo?
hd1 06.03.2019 05:02

Будет ли это работать, если вы поместите __init__.py в свой каталог верхнего уровня?

jnrbsn 06.03.2019 05:07

@hd1: from . import parse приводит к ImportError: cannot import name 'parse'

snazzybouche 06.03.2019 05:13

@jnrbsn: К сожалению, это не так

snazzybouche 06.03.2019 05:14
stackoverflow.com/a/50193944/674039 - это ответ.
wim 06.03.2019 05:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
5
2 771
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете удалить точки, и это должно работать:

# parsemessages.py
from helpers import parse
parse.execute("string to be parsed")

Это, вероятно, ваше лучшее решение, если вы действительно не хотите делать это пакетом. Вы также можете вложить весь проект на один каталог глубже и назвать его как python3 foo/bot.py.

Объяснение:

Когда вы не работаете с фактически установленным пакетом, а просто импортируете данные, относящиеся к вашему текущему рабочему каталогу, все в этом каталоге считается пакетом верхнего уровня. В вашем случае bot, plugins, helpers и commands все пакеты/модули верхнего уровня. Ваш текущий рабочий каталог сам по себе является нет пакетом.

Поэтому, когда вы делаете...

from ..helpers import parse

... helpers считается пакетом верхнего уровня, потому что он находится в вашем текущем рабочем каталоге, и вы пытаетесь импортировать его из одного уровня выше этого (от вашего текущего рабочего каталога сам, который не является пакетом).

Когда ты делаешь...

from .helpers import parse

... вы импортируете относительно plugins. Итак, .helpers решается на plugins.helpers.

Когда ты делаешь...

from helpers import parse

... он находит helpers как пакет верхнего уровня, потому что он находится в вашем текущем рабочем каталоге.

Если вы хотите выполнить свой код из корня, мой лучший ответ на это — добавить в путь вашу корневую папку с помощью os.getcwd(). Убедитесь, что в вашей родственной папке есть файл в этом.py.

import os
os.sys.path.insert(0, os.getcwd())

from sibling import module

Другие вопросы по теме