Імпортувати файл із підкаталогу?


455

У мене файл, який називається 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з підкаталогу?

EDIT

__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 відзначає безпечні / доступні каталоги імпорту.
IАнотація

7
Він не тільки позначає безпечні / доступні каталоги імпорту, але й надає спосіб запустити деякий код ініціалізації при імпорті імені каталогу.
Саджад

32
Так, це хакітно і навіть брудно, і на мою думку, мова не повинна нав'язувати спосіб завантаження файлів у файлову систему. У PHP ми вирішили цю проблему, дозволивши коду користувача зареєструвати кілька функцій автоматичного завантаження, які викликаються, коли простір імен / клас відсутній. Тоді громада виробила стандарт PSR-4, і Composer впроваджує його, і нині ніхто про це не турбується. І жодних дурних __init__файлів з твердим кодом (але якщо ви цього хочете, просто зареєструйте гачок для автоматичного завантаження! Це різниця між хакі і хакерськими ).
Morgan Touverey Quilling

4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman

4
python - безладний :)
Джиммі Петтерссон

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!

2
Чи можете ви надати посилання на документацію Python, де це пояснено? Дякую!
Зенон

5
Давайте зробимо цей посилання клікабельним: docs.python.org/3/tutorial/modules.html#packages
Габріель

Приємний посібник для реалізації пакетуlib
MasterControlProgram

зверніть увагу: підкаталоги не повинні містити тире або крапки, але підкреслення дійсні. для мене це здається тими ж обмеженнями, що й для інших імен символів, але я ще не перекопав це до рівня документації.
Олександр Стор

підкреслює => python3 (занадто пізно для редагування коментаря)
Олександр Стор

68

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

sys.path.insert(0, './lib')
import BoxTime

11
Це чудово, якщо ви з якихось причин не можете або не створите файл init .py.
jpihl

1
Він працює, якщо ви запускаєте python з каталогу "project". "". інтерпретується відносно вашої поточної робочої директорії, а не щодо каталогу, в якому живе файл, який ви виконуєте. Скажімо вам cd /data, python ../project/tester.py. Тоді це не спрацює.
ранкова

2
Це працювало для мене. Я віддаю перевагу цьому над файлом init .py, він робить для більш чистих заяв про імпорт.
Тейлор Евансон

5
Це працює набагато краще і є "правильним" рішенням. init .py псує пакети, такі як boto, які мають власні дочірні папки з модулями.
Дейв Допсон

1
@jpihl Ви повинні створити (принаймні) емпі-файл із назвою __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 .


2
Я не думаю, що я ніколи не бачив того синтаксису, який раніше використовувався. Чи є вагомі причини (не) використовувати цей метод?
tgray

2
Чому це не було відповіддю. Звичайно, якщо ви хочете зробити цілу справу з пакетами, ви повинні це зробити. Але це не те, що було в первісному питанні.
Тревіс Гріггс

Це дає мені: ValueError: Спроба відносного імпорту в непакеті
Alex

5
Це працює лише в тому випадку, якщо файл, з якого ви імпортуєте, сам є частиною пакету. Якщо ні, ви отримаєте помилку, яку вказав @Alex.
Джонатан Райнхарт

8

Я роблю це, що в основному охоплює всі випадки (переконайтеся, що ви маєте __init__.pyвідносну / шлях / до / ваш / 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')замість жорсткого кодування '/' в конкатенації шляху.
коуберт

Чому ти просто не можеш обійтися "../anotherproject"без цього os.path.dirname()?
Моше Рабаєв

@MosheRabaev - Доброю практикою є використання функцій os.path. У разі запускання "../anotherproject" та переміщення коду до ОС Windows, код порушиться! os.path утиліти знають, як повернути правильний шлях, враховуючи ОС, на якій працює код. для отримання додаткової інформації docs.python.org/2/library/os.path.html
Меркурій,

@MosheRabaev і якщо ви будете використовувати ".." без цього dirname(realpath(__file__)), то він буде обчислювати шлях відносно вашого поточного робочого каталогу під час запуску сценарію, а не відносно того, де живе сценарій.
TJ Ellis

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()

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- Потреби мають два елементи один, __init__.pyа в каталозі з назвою деяка папка #somefolderє два елементи boxtime.pyта__init__.py


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