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


1410

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

application/app/folder/file.py

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

application/app2/some_folder/some_file.py

я пробовал

from application.app.folder.file import func_name

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



Чтение официальной документации мне очень помогло! 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

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('../')

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%

Подождите, я бы заменил мои скрипты с именем файла?
Владимир Путин

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

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


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.


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


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

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


Проблемы? Если это не работает для вас, то вы, вероятно, получаете немного от относительного импорта многих приобретенных товаров. Прочитайте ответы и комментарии для получения более подробной информации: Как исправить «Попытка относительного импорта в неупакованном виде» даже с __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')
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 .


13

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

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


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

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

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


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, который представляет собой приведенный выше код, затем папку с именем Scripts, и затем вы можете вызывать все, что вам нужно, из этой папки, изменив 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.