Імпорт файлів з іншої папки


1402

У мене є така структура папок.

application/app/folder/file.py

і я хочу імпортувати деякі функції з file.py в інший файл Python, який знаходиться в

application/app2/some_folder/some_file.py

Я намагався

from application.app.folder.file import func_name

та деякі інші різні спроби, але поки що мені не вдалося належним чином імпортувати. Як я можу це зробити?


2
wim

Читання офіційної документації мені дуже допомогло! docs.python.org/3/reference/…
Kavin Raju S

Відповіді:


1437

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

Однак ви можете додати до шляху Python під час виконання:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file

355
sys.path.append('/path/to/application/app/folder')чистіше imo
псевдосудо

387
@pseudosudo: Так, але якщо вставити його на початку, ви можете гарантувати, що шлях буде шуканий раніше (навіть вбудований) у випадку конфліктування імен.
Камерон

8
@kreativitea - sys.pathповертає a list, а не a deque, і було б нерозумно перетворитиlist на a dequeі назад.
ArtOfWarfare

37
Чи вважається це пітонічним способом управління .py файлами у папках? Мені цікаво ... чому він не підтримується за замовчуванням? не має сенсу підтримувати всі .py файли в одній директорії ..
Ofir

49
@Ofir: Ні, це не приємне чисте пітонічне рішення. Загалом, ви повинні використовувати пакети (які базуються на деревах директорій). Ця відповідь була специфічною для поставленого питання, і чомусь продовжує нараховуватися велика кількість відгуків.
Камерон

709

Нічого поганого в:

from application.app.folder.file import func_name

Просто переконайтесь folder також містить __init__.py, це дозволяє включити його як пакет. Не впевнений, про що говорять інші відповіді PYTHONPATH.


47
Тому що це не стосується випадків, коли PYTHONPATHнеобхідні зміни . Скажіть, у вас дві папки на одному рівні: Aі B. Aмає __init.py__. Спробуйте імпортувати щось Bізсередини A.
msvalkon

32
Що знаходиться всередині init.pyабо у __init__.pyфайлі?
Лорем Іпсум Долор

47
@Xinyang Це може бути порожній файл. Саме його існування підказує Python трактувати каталог як пакет.
сойка

9
Звичайно , ця відповідь передбачає , що applicationі appпакети вже (тобто у вас вже є __init__.pyв обох з них). Як результат додавання __init__.pyтакож до folder, application.app.folderстає (під) пакетом , з якого ви можете отримати доступ до модуля application.app.folder.file , символ якого func_nameтепер можна імпортувати
Yibo Yang

29
Що б я не спробував, це не вийде. Я хочу імпортувати з каталогу "братів", так що один вгору вниз. Усі мають __ init __. Py's, включаючи батьківські. Це пітон 3-специфічний?
dasWesen

109

Коли модулі знаходяться в паралельних місцях, як у питанні:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Ця стенограма робить один модуль видимим для іншого:

import sys
sys.path.append('../')

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

14
Щоб уникнути цього, ми можемо отримати батьківський каталог файлуsys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul

Це не спрацювало для мене - мені довелося додати додаткове ім’я dirname, щоб піднятися назад до батьківського, щоб біг cli/foo.pyіз командного рядка змігimport cli.bar
RCross

2
@Rahul, ваше рішення не працює для інтерактивних оболонок
towi_parallelism

Якщо ви запускаєте його з вашої кореневої папки (тобто папки додатків), ви, ймовірно, добре, sys.path.append('.')потім імпортувати модуль за допомогою from app2.some_folder.some_file import your_function. Крім того, те, що для мене працює, працює python3 -m app2.another_folder.another_fileз кореневою папкою.
залежний

68

Перший імпорт системних файлів у name-file.py

 import sys

Друге додайте шлях до папки у name-file.py

sys.path.insert(0, '/the/folder/path/name-package/')

Третє Створіть у своєму підкаталозі пустий файл під назвою __ init __.py (це повідомляє Python, що це пакет)

  • name-file.py
  • ім'я-пакет
    • __ init __.py
    • name-module.py

Четвертий імпорт модуля всередині папки в name-file.py

from name-package import name-module

5
Якщо папка з назвою знаходиться прямо під name-file.py, це має працювати навіть без команди sys.path.insert-command. Таким чином, відповідь залишає питання, якщо це рішення працює навіть тоді, коли папка з іменами знаходиться в довільному місці.
Бастіан

55

Я думаю, що спеціальним способом було б використання змінної середовища,PYTHONPATH як описано в документації: Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%

Зачекайте, я заміню myScripts на ім'я файлу?
Володимир Путін

4
ні, зі шляху до каталогу до вашого .py-файлу
Ax3l

1
На жаль, якщо ви використовуєте Anaconda, це не буде працювати, оскільки під кришкою PYTHONPATH насправді не використовується внутрішньо!
information_interchange

Щодо (нещодавніх) змін у анаконді, дивіться цю ПЗ щодо робочих процесів та коментарів для робочих обстановок: stackoverflow.com/questions/17386880/… Взагалі кажучи, створюйте та встановлюйте невеликі пакети замість злому імпортних режимів.
Ax3l

46

Відповіді тут недостатньо чіткі, це перевірено на Python 3.6

З цією структурою папок:

main.py
|
---- myfolder/myfile.py

Де myfile.pyвміст:

def myfunc():
    print('hello')

Заява про імпорт у main.py:

from myfolder.myfile import myfunc
myfunc()

і це надрукує привіт .


9
додавання файлу конфігурації init .py (порожній) у мою папку працював на мене на linux (y)
Вінсент

7
@Вінсенс ти мав на увазі __init__.py?
мрглум

2
@mrgloom дійсно
Вінсент

3
Чомусь додавання __init__.pyне працює для мене. Я використовую Py 3.6.5 для Ubuntu 18. Він працює на Pycharm, але не з терміналу
Crearo Rotar

9
Це абсолютно не пов'язане з питанням, яке задає питання про імпорт файлів з іншої гілки дерева файлів, ніж поточна робоча директорія.
Олександр Росса

44

Ваша проблема полягає в тому, що Python шукає цей каталог у файлі Python, а не знаходить його. Ви повинні вказати, що ви говорите про каталог, який ви перебуваєте, а не про Python.

Для цього ви змінюєте це:

from application.app.folder.file import func_name

до цього:

from .application.app.folder.file import func_name

Додаючи крапку, яку ви говорите, шукайте в цій папці папку додатків, а не шукайте в каталозі Python.


2
це те, що мене зафіксувало
Фі

32

З того, що мені відомо, додавання __init__.pyфайла безпосередньо в папку функцій, які ви хочете імпортувати, зробить роботу.


7
лише якщо сценарій, який хоче включити цей інший каталог, вже є на sys.path
Ax3l

2
Я використовував sys.path.append(tools_dir)у Windows, і мені не потрібно додавати __init__.py' file in my directory tools_dir`
herve-guerin

25

У Python 3.4 та пізніших версіях ви можете імпортувати безпосередньо з вихідного файлу безпосередньо (посилання на документацію) . Це не найпростіше рішення, але я включаю цю відповідь для повноти.

Ось приклад. Спочатку файл, який потрібно імпортувати, названий foo.py:

def announce():
    print("Imported!")

Код, який імпортує файл вище, натхненний прикладом документації:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

Вихід:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Зверніть увагу, що ім'я змінної, ім'я модуля та ім'я файлу не повинні збігатися. Цей код все ще працює:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

Вихід:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Програмно імпорт модулів був введений в Python 3.1 і дає вам більше контролю над тим, як імпортуються модулі. Для отримання додаткової інформації зверніться до документації.


11
Я не знаю, чи хтось навіть намагався це зрозуміти, але я думаю, що це занадто складно.
Дан

23

Працював для мене в python3 на Linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  

6
sys.path.append("/home/linux/folder/")- Обов’язково не використовуйте ярлик, наприклад"~/folder/"
daGo

22

Спробуйте відносний імпорт Python:

from ...app.folder.file import func_name

Кожна провідна точка - це ще один вищий рівень в ієрархії, починаючи з поточного каталогу.


Проблеми? Якщо це не працює для вас, то, ймовірно, ви переживаєте багато відносних імпортів gotcha. Прочитайте відповіді та коментарі для отримання більш детальної інформації: Як виправити "Спроба відносного імпорту в непакеті" навіть за допомогою __init__.py

Підказка: мати __init__.pyна кожному рівні каталогу. Можливо, вам знадобиться python -m application.app2.some_folder.some_file(залишити .py), який ви запускаєте з каталогу верхнього рівня або мати цей каталог верхнього рівня у вашому PYTHONPATH. Фу!


22

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

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name

2
Ви відповідаєте, було б корисніше, якби ви могли пояснити, що це відрізняється від звичайного імпорту?
not2qubit

1
У мене були /path/dir1/__init__.py та /path/dir1/mod.py. Для /path/some.py з dir1.mod функціонував імпорт. Коли в /path/dir2/some.py він працював лише після того, як я скопіював і вставив вищевказану відповідь у верхній частині файлу. Не хотів редагувати мій шлях, оскільки не кожен проект python є в / path /.
jwal

19

Розглядаючи в applicationякості кореневого каталогу для вашого пітона проекту, створіть порожній __init__.pyфайл application, appі folderпапку. Потім введіть такі some_file.pyзміни, щоб отримати визначення функції func_name:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()

має бути: sys.path.insert (0, r '/ from / root / directory')
Болака

18

Використання sys.path.append з абсолютним шляхом не є ідеальним під час переміщення програми в інші середовища. Використання відносного шляху не завжди працюватиме, оскільки поточний робочий каталог залежить від того, як викликався сценарій.

Оскільки структура папки додатків виправлена, ми можемо використовувати os.path, щоб отримати повний шлях модуля, який ми хочемо імпортувати. Наприклад, якщо це структура:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

І скажімо, що ви хочете імпортувати модуль "манго". Ви можете зробити наступне в vanilla.py:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Звичайно, вам не потрібна змінна mango_dir.

Щоб зрозуміти, як це виглядає на цьому прикладі інтерактивного сеансу:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

І перевірте документацію на os.path .


13

Це працює для мене на windows

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file

10

Я зовсім особливий: я використовую Python з Windows!

Я просто завершую інформацію: і для Windows, і для Linux працюють як відносний, так і абсолютний шлях sys.path(мені потрібні відносні шляхи, оскільки я використовую свої сценарії на декількох ПК та в різних основних каталогах).

І коли ви використовуєте Windows, \і /їх можна використовувати як роздільник імен файлів, і, звичайно, ви повинні подвоїтись \у рядки Python,
кілька дійсних прикладів:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(зауважте: я вважаю, що /це зручніше, ніж \, подія, якщо воно менш "рідне для Windows", оскільки воно сумісне з Linux і простіше писати та копіювати в Windows Explorer)


4
os.path.join ('інструменти', 'mydir')
Corey Goldberg

7

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

Я тестував це на Linux, але він повинен працювати в будь-якій сучасній ОС, яка підтримує символічні посилання.

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


7

У моєму випадку у мене був клас для імпорту. Мій файл виглядав так:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

У свій основний файл я включив код через:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper

1
@ not2qubit sys не було імпортовано у відповідь.
Вальтер

7

Я наткнувся на одне і те ж питання кілька разів, тому хотів би поділитися своїм рішенням.

Версія Python: 3.X

Наступне рішення призначене для тих, хто розробляє вашу програму у версії 3.X Python, оскільки Python 2 не підтримується з січня / 1/2020 .

Структура проекту

У python 3 вам не потрібен __init__.pyпідкаталог вашого проекту через невідповідні пакети простору імен . Див. Чи init .py не потрібен для пакетів в Python 3.3+

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Постановка проблеми

У file_b.py, я хотів би імпортувати клас Aв file_a.pyпапку a.

Рішення

№1 Швидкий, але брудний спосіб

Не встановлюючи пакет, як ви зараз розробляєте новий проект

Використовуючи try catchдля перевірки наявності помилок. Приклад коду:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

№2 Встановіть свій пакет

Після встановлення програми (у цій публікації підручник із встановленням не входить)

Можна просто

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

Щасливого кодування!


для отримання додаткової інформації про абсолютний імпорт
WY Hsu

3

Я працював над проектом, aякий я хотів, щоб користувачі встановлювали за pip install aдопомогою наступного списку файлів:

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

a / a.py

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

a / b / b.py

dogs = 1

Я встановив модуль, запустивши з каталогу наступне MANIFEST.in:

python setup.py install

Тоді /moustache/armwrestleя міг запуститись із зовсім іншого розташування моєї файлової системи :

import a
dir(a)

Що підтвердило, що a.catsдійсно дорівнювало 0 і a.b.dogsдійсно дорівнювало 1, як задумано.


2

Замість того, щоб просто робити import ..., зробіть це:

from <MySubFolder> import <MyFile>

MyFile знаходиться всередині MySubFolder.


1

Ви можете оновити оболонку Python, натиснувши клавішу f5, або перейдіть до пункту Run-> Run Module. Таким чином, вам не потрібно змінювати каталог, щоб прочитати щось із файлу. Python автоматично змінить каталог. Але якщо ви хочете працювати з різними файлами з різного каталогу в оболонці Python, то ви можете змінити каталог в sys, як Камерон сказав раніше.


1

Тож я просто правою кнопкою миші натиснув свій IDE і додав новий folderі цікавився, чому мені не вдалося імпортувати з нього. Пізніше я зрозумів, що мені потрібно клацнути правою кнопкою миші та створити пакет Python, а не класичну папку файлової системи. Або післясмертний метод додавання __init__.py(що змушує python трактувати папку файлової системи як пакет), як згадується в інших відповідях. Додайте сюди цю відповідь про всяк випадок, якщо хтось пішов цим шляхом.


1

Ви можете використовувати importlib для імпорту модулів, куди потрібно імпортувати модуль із папки, використовуючи такий рядок:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

У цьому прикладі є main.py, який є вищевказаним кодом, а потім папкою під назвою Сценарії, і ви можете зателефонувати все, що вам потрібно з цієї папки, змінивши scriptNameзмінну. Потім ви можете використовувати scriptдля посилання на цей модуль. наприклад, якщо у мене є функція, що викликається Hello()в модулі Snake, ви можете запустити цю функцію, зробивши це:

script.Hello()

Я перевірив це в Python 3.6


0

У мене були ці проблеми неодноразово. Я багато заходжу на цю ж сторінку У своїй останній проблемі мені довелося запускати serverфайл із фіксованого каталогу, але щоразу, коли налагодження я хотів запускати з різних підкаталогів.

import sys
sys.insert(1, /path) 

зробив НЕ працювати для мене , тому що в різних модулях я повинен був прочитати інше * .csv файли , які були все в тому ж каталозі.

Зрештою, те, що працювало для мене, не було пітонічним, я думаю, але:

Я використав if __main__ поверх модуля, який хотів налагодити , тобто запускається з іншого, ніж зазвичай, шляху.

Тому:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.