Импортировать файл из подкаталога?

456

У меня есть файл с именем tester.py, расположенный на /project.

/projectимеет подкаталог libс именем файла BoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

Я хочу импортировать BoxTimeиз tester. Я пробовал это:

import lib.BoxTime

В результате чего:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

Есть идеи как импортировать BoxTimeиз подкаталога?

РЕДАКТИРОВАТЬ

Проблема __init__.pyбыла, но не забывайте ссылаться на BoxTimeнее lib.BoxTimeили использовать:

import lib.BoxTime as BT
...
BT.bt_function()
Адам Матан
источник

Ответы:

536

Ознакомьтесь с документацией по пакетам (раздел 6.4) здесь: http://docs.python.org/tutorial/modules.html.

Короче нужно поставить пустой файл с именем

__init__.py

в каталоге "lib".

Грег
источник
59
Почему это чувствуется хаки ? Это способ, которым python отмечает безопасные / доступные каталоги импорта.
IAbstract
7
Он не только помечает безопасные / доступные каталоги импорта, но также предоставляет способ запуска некоторого кода инициализации при импорте имени каталога.
Саджад
32
Да, это глупо и даже грязно, и, по моему мнению, язык не должен навязывать способ загрузки файлов через файловую систему. В PHP мы решили эту проблему, позволив коду пользовательского кода регистрировать несколько автозагрузочных функций, которые вызываются, когда отсутствует пространство имен / класс. Тогда сообщество разработало стандарт PSR-4, и Composer его реализует, и в настоящее время никто не должен беспокоиться об этом. И никаких глупых жестко закодированных __init__файлов (но если вы хотите, просто зарегистрируйте автозагрузку хука! В этом разница между хакером и хакером ).
Морган Тувери Квиллинг
4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman
4
Питон грязный :)
Джимми Петтерссон
174
  • Создайте подкаталог с именем lib.
  • Создайте пустой файл с именем lib\__init__.py.
  • В lib\BoxTime.py, напишите функцию, foo()как это:

    def foo():
        print "foo!"
  • В коде вашего клиента в каталоге выше libнапишите:

    from lib import BoxTime
    BoxTime.foo()
  • Запустите ваш клиентский код. Ты получишь:

    foo!

Намного позже - в Linux это будет выглядеть так:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!
hughdbrown
источник
2
Не могли бы вы предоставить ссылку на документацию по Python, где это объясняется? Спасибо!
Зенон
5
Давайте сделаем эту ссылку кликабельной: docs.python.org/3/tutorial/modules.html#packages
Габриэль Стейплс
lib
MasterControlProgram,
обратите внимание: подкаталоги не должны содержать тире или точек, но подчеркивания действительны. для меня это похоже на те же ограничения, что и для других имен символов, но я еще не докопался до уровня документации.
Александр Стор
подчеркивание => python3 (слишком поздно для редактирования комментария)
Александр Stohr
68

Вы можете попробовать вставить его в sys.path:

sys.path.insert(0, './lib')
import BoxTime
Kresimir
источник
11
Это замечательно, если вы по какой-то причине не можете или не хотите создавать файл init .py.
Jpihl
1
Это работает, если вы запускаете python из каталога "project". "." интерпретируется относительно вашего текущего рабочего каталога, а не относительно каталога, в котором находится исполняемый файл. Скажем вам cd /data, python ../project/tester.py. Тогда это не сработает.
morningstar
2
Это сработало для меня. Я предпочитаю это файлу init .py, это делает для более чистых операторов импорта.
Тейлор Эвансон
5
Это работает НАМНОГО лучше и является «правильным» решением. init .py портит пакеты типа boto, которые имеют свои дочерние папки с модулями.
Дейв Допсон
1
@jpihl Вы должны создать (как минимум) файл empy с именем __init__.py, чтобы разрешить импорт модулей python из этой папки. Я пробовал это решение и работает отлично (v2.7.6).
m3nda
31

Я записываю это, потому что каждый, кажется, предлагает вам создать libкаталог.

Вам не нужно называть свой подкаталог lib. Вы можете назвать его anythingпри условии, что вы положили __init__.pyв него.

Вы можете сделать это, введя следующую команду в оболочке Linux:

$ touch anything/__init__.py 

Итак, теперь у вас есть эта структура:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Тогда вы можете импортировать mylibв main.pyэто:

from anything import mylib 

mylib.myfun()

Вы также можете импортировать функции и классы следующим образом:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

Любая переменная функция или класс, который вы размещаете внутри, __init__.pyтакже могут быть доступны:

import anything

print(anything.myvar)

Или вот так:

from anything import myvar

print(myvar)
Нуреттин
источник
Моя структура папок есть utils\__init__.pyи utils\myfile.py. (Утилиты содержат оба файла). Вот как я пытаюсь импортировать from utils.myfile import myMethod. Но я получаю ModuleNotFoundError: No module named 'utils'. Что может быть не так? PS: я использую Djangoи пытаюсь импортировать, в views.pyкотором находится на том же уровне, что и utilsпапка
Jagruti
Можно использовать абсолютные пути при импорте модулей и запустить вашу программу с помощьюPYTHONPATH=. python path/to/program.py
nurettin
21

Ваш каталог lib содержит __init__.pyфайл?

Python использует __init__.pyдля определения, является ли каталог модулем.

брод
источник
16

Попробуй import .lib.BoxTime. Для получения дополнительной информации читайте об относительном импорте в PEP 328 .

drrlvn
источник
2
Я не думаю, что когда-либо видел такой синтаксис, используемый раньше. Есть ли веская причина (не) использовать этот метод?
tgray
2
Почему это не был ответ. Конечно, если вы хотите сделать целые пакеты, вы должны это сделать. Но это не то, чем был первоначальный вопрос.
Трэвис Григгс
Это дает мне: ValueError: Попытка относительного импорта в неупакованном виде
Алекс
5
Это работает, только если файл, из которого вы импортируете, сам является частью пакета. Если нет, вы получите ошибку, на которую указал @Alex.
Джонатон Рейнхарт
8

Я делаю это, что в основном охватывает все случаи (убедитесь, что у вас есть __init__.pyв папке / path / to / your / lib /):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


Пример: у
вас есть в папке проекта:

/root/myproject/app.py

У вас есть в другой папке проекта:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

Вы хотите использовать /root/anotherproject/utils.py и вызывать функцию foo, которая есть в ней.

Итак, вы пишете в app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()
Меркурий
источник
2
если вы используете, os.pathвы, вероятно, захотите использовать os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')вместо жесткого кодирования '/' в конкатенации вашего пути.
Cowbert
Почему ты не можешь просто обойтись "../anotherproject"без os.path.dirname()?
Моше Рабаев
@MosheRabaev - Хорошей практикой является использование функций os.path. В случае написания "../anotherproject" и переноса кода в ОС Windows, код сломается! Утилиты os.path знают, как вернуть правильный путь, учитывая ОС, на которой выполняется код. для получения дополнительной информации docs.python.org/2/library/os.path.html
Меркурий
@MosheRabaev, и если вы используете «..» без dirname(realpath(__file__)), то при запуске скрипта он вычислит путь относительно вашего текущего рабочего каталога, а не относительно того, где он находится.
Ти Джей Эллис
5

Создайте пустой файл __init__.pyв подкаталоге / lib. И добавить в начале основной код

from __future__ import absolute_import 

тогда

import lib.BoxTime as BT
...
BT.bt_function()

или лучше

from lib.BoxTime import bt_function
...
bt_function()
Mik
источник
0

Просто дополнение к этим ответам.

Если вы хотите импортировать все файлы из всех подкаталогов , вы можете добавить это в корень вашего файла.

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

И тогда вы можете просто импортировать файлы из подкаталогов так, как если бы эти файлы были внутри текущего каталога.

Рабочий пример

Если у меня есть следующий каталог с подкаталогами в моем проекте ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

Я могу поместить следующий код в мой a.pyфайл

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

Другими словами, этот код будет абстрагироваться от того, из какого каталога идет файл.

Виктор
источник
-1

/project/tester.py

/project/lib/BoxTime.py

создайте пустой файл __init__.pyвниз по строке, пока не дойдете до файла

/project/lib/somefolder/BoxTime.py

#lib- needs содержит два элемента one, __init__.pyа каталог с именем somefolder #somefolderсодержит два элемента boxtime.pyи__init__.py

Чайтанья Гк
источник
-3

попробуй это:

from lib import BoxTime

Оранэ
источник
8
без каких-либо объяснений это не очень полезно.
Жан-Франсуа Фабр