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

1410

У меня есть следующая структура папок.

application/app/folder/file.py

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

application/app2/some_folder/some_file.py

я пробовал

from application.app.folder.file import func_name

и некоторые другие различные попытки, но до сих пор я не смог правильно импортировать. Как я могу это сделать?

Иван
источник
2
Связанный: stackoverflow.com/q/43476403/674039
Вим
Чтение официальной документации мне очень помогло! docs.python.org/3/reference/…
Кавин Раджу С.

Ответы:

1447

По умолчанию вы не можете. При импорте файла 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
Cameron
источник
357
sys.path.append('/path/to/application/app/folder')чище имо
псевдосудо
388
@pseudosudo: Да, это так, но вставка его в начале дает преимущество, заключающееся в том, что в случае конфликтов имен происходит поиск пути перед другими (даже встроенными).
Кэмерон
8
@kreativitea - sys.pathвозвращает a list, а не a deque, и было бы глупо конвертировать в lista dequeи обратно.
ArtOfWarfare
37
Считается ли это питонским способом управления файлами .py в папках? Мне интересно ... почему это не поддерживается по умолчанию? нет смысла хранить все файлы .py в одном каталоге ..
Ofir
49
@Ofir: Нет, это не хорошее чистое питоническое решение. В общем, вы должны использовать пакеты (которые основаны на деревьях каталогов). Этот ответ был специфичен для заданного вопроса, и по какой-то причине продолжает набирать большое количество голосов.
Кэмерон
710

Ничего плохого в:

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файл?
Lorem Ipsum Dolor
48
@Xinyang Это может быть пустой файл. Само его существование говорит Python рассматривать каталог как пакет.
сойка
9
Конечно, этот ответ предполагает, что applicationи appпакеты уже (то есть у вас уже есть __init__.pyв обоих). В результате добавления __init__.pyтакже кfolder , application.app.folderстановится (суб) пакет , из которого можно получить доступ к модулю application.app.folder.file , символ которой func_nameмогут быть импортированы
Yibo Yang
30
Что бы я ни пытался, это не сработает. Я хочу импортировать из одноуровневого каталога, так что один вверх один вниз. У всех есть __ init __. Py, в том числе родительский. Этот питон 3-специфичен?
dasWesen
111

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

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

Это сокращение делает один модуль видимым для другого:

import sys
sys.path.append('../')
slizb
источник
24
Предостережение: это работает до тех пор, пока скрипт импорта запускается из каталога, в котором он находится. В противном случае родительский каталог любого другого каталога, из которого запускается скрипт, будет добавлен к пути и импорт завершится неудачно.
Карл Смит
14
Чтобы избежать этого, мы можем получить родительский каталог файлаsys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul
Это не сработало для меня - мне пришлось добавить туда дополнительное имя, чтобы подняться обратно к родителю, чтобы запустить 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

Первый импорт sys в 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
Если имя-папки находится прямо под именем -file.py, это должно работать даже без sys.path.insert-команды. Таким образом, ответ оставляет вопрос, работает ли это решение, даже если имя-папка находится в произвольном месте.
Бастиан
55

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

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

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
источник
Подождите, я бы заменил мои скрипты с именем файла?
Владимир Путин
4
нет, с
указанием
1
К сожалению, если вы используете Anaconda, это не сработает, так как под капотом PYTHONPATH действительно не используется внутри!
информационный
Для (недавних) изменений в anaconda см. Этот SO для рабочих процессов и комментарии для обходных путей: stackoverflow.com/questions/17386880/… Вообще говоря, собирайте и устанавливайте небольшие пакеты вместо взлома каталогов импорта.
Ax3l
47

Ответы здесь не ясны, это проверено на Python 3.6

С этой структурой папок:

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

Где myfile.pyнаходится содержание:

def myfunc():
    print('hello')

Оператор импорта в main.py:

from myfolder.myfile import myfunc
myfunc()

и это напечатает привет .

danday74
источник
9
добавление init .py (пустого) файла конфигурации в myfolder для меня работало на Linux (Y)
Винсент
7
@ Винсент ты имел ввиду __init__.py?
mrgloom
2
@mrgloom действительно
Винсент
3
По некоторым причинам добавление __init__.pyне работает для меня. Я использую Py 3.6.5 на Ubuntu 18. Он работает на Pycharm, но не с терминала
Crearo Rotar
9
Это совершенно не связано с вопросом, который задает вопрос об импорте файлов из другой ветви дерева файлов, чем текущий рабочий каталог.
Александр Росса
45

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

Для этого вы измените это:

from application.app.folder.file import func_name

к этому:

from .application.app.folder.file import func_name

Добавляя точку, вы говорите, ищите в этой папке папку приложения, а не ищите в каталоге Python.

CianB
источник
2
это то, что исправило меня
Phi
32

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

Вайбхав Сингх
источник
7
только если скрипт, который хочет включить этот другой каталог, уже находится в sys.path
Ax3l
2
Я использовал sys.path.append(tools_dir)на Windows, и мне не нужно добавлять __init__.py' file in my directory tools_dir`
herve-
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 и дает вам больше контроля над тем, как импортируются модули. Обратитесь к документации для получения дополнительной информации.

wecsam
источник
11
Я не знаю, пытался ли кто-нибудь понять это, но я думаю, что это слишком сложно.
Дан
23

У меня работал в python3 на linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
источник
6
sys.path.append("/home/linux/folder/")- Убедитесь, что не используете ярлык, например"~/folder/"
daGo
22

Попробуйте относительный импорт Python:

from ...app.folder.file import func_name

Каждая начальная точка - это еще один более высокий уровень в иерархии, начинающийся с текущего каталога.


Проблемы? Если это не работает для вас, то вы, вероятно, получаете немного от относительного импорта многих приобретенных товаров. Прочитайте ответы и комментарии для получения более подробной информации: Как исправить «Попытка относительного импорта в неупакованном виде» даже с __init__.py

Подсказка: есть __init__.pyна каждом уровне каталогов. Вам может понадобиться python -m application.app2.some_folder.some_file(не включая .py), который вы запускаете из каталога верхнего уровня или иметь этот каталог верхнего уровня в вашей PYTHONPATH. Уф!

Zectbumo
источник
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()
ChandanK
источник
должно быть: sys.path.insert (0, r '/ from / root / directory')
Bolaka
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 .

Nagev
источник
13

Это работает для меня на окнах

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

import some_file
Emeeus
источник
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)

Эрве-Герена
источник
4
os.path.join ('tools', 'mydir')
Кори Голдберг
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
schmudu
источник
1
@ not2qubit sys не был импортирован в ответ.
Уолтер
7

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

Версия Python: 3.X

Следующее решение предназначено для тех, кто разрабатывает ваше приложение на Python версии 3.X, поскольку 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папке а.

Решения

# 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
источник
для получения дополнительной информации об абсолютном импорте
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 *.*

/ init .py

from __future__ import absolute_import

from a.a import cats
import a.b

а / 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, как и предполагалось.

duhaime
источник
2

Вместо того, чтобы просто делать import ..., сделайте это:

from <MySubFolder> import <MyFile>

MyFile находится внутри MySubFolder.

Galileoomega
источник
1

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

IOstream
источник
1

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

mithunpaul
источник
1

Вы можете использовать importlib для импорта модулей, в которые вы хотите импортировать модуль из папки, используя следующую строку:

import importlib

scriptName = 'Snake'

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

В этом примере есть main.py, который представляет собой приведенный выше код, затем папку с именем Scripts, и затем вы можете вызывать все, что вам нужно, из этой папки, изменив scriptNameпеременную. Затем вы можете использовать scriptссылку на этот модуль. например, если у меня есть функция, вызываемая Hello()в модуле Snake, вы можете запустить эту функцию следующим образом:

script.Hello()

Я проверил это в Python 3.6

Dextron
источник
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')
Б Фуртадо
источник