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

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

application/app/folder/file.py

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

application/app2/some_folder/some_file.py

я пробовал

from application.app.folder.file import func_name

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

42 ответа

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

Тем не менее, вы можете добавить к пути Python во время выполнения:

# some_file.py
import sys
sys.path.insert(0, '/path/to/application/app/folder')

import file

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

from application.app.folder.file import func_name

Просто убедитесь, что папка также содержит __init__.py, это позволяет ей быть включенным в пакет. Не уверен, почему другие ответы говорят о PYTHONPATH.

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

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

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

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

Первый импорт системы

 import sys

Второй добавить путь к папке

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

В-третьих, создайте пустой файл с именем __ init __.py в вашем подкаталоге (это говорит Python, что это модуль)

  • name-file.py
    • имя-папки
      • __ init __.py
      • name-module.py

Четвертый импорт модуля внутри папки

from name-folder import name-module

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

from ...app.folder.file import func_name

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


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

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

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

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

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

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

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

from application.app.folder.file import func_name

к этому:

from .application.app.folder.file import func_name

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

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

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

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

куда myfile.py имеет содержание:

def myfunc():
    print('hello')

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

from myfolder.myfile import myfunc
myfunc()

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

В Python 3.4 и более поздних версиях вы можете импортировать напрямую из исходного файла (ссылка на документацию).

Вот пример. Сначала импортируемый файл с именем foo.py:

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

Код, который импортирует файл выше, в значительной степени вдохновлен примером в документации:

import importlib, importlib.util, os.path

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, importlib.util, os.path

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

Использование 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.

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

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

import os, sys

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

from root_folder import file_name

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

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

Принимая во внимание application в качестве корневого каталога для вашего проекта Python, создайте пустой __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()
      ├───root
│   ├───dir_a
│   │   ├───file_a.py
│   │   └───file_xx.py
│   ├───dir_b
│   │   ├───file_b.py
│   │   └───file_yy.py
│   ├───dir_c
│   └───dir_n

Вы можете добавить родительский каталог в PYTHONPATHдля этого вы можете использовать путь, зависящий от ОС, в «пути поиска модуля», который указан в sys.path. Таким образом, вы можете легко добавить родительский каталог, как показано ниже:

      # file_b.py

import sys
sys.path.insert(0, '..')

from dir_a.file_a import func_name

Лучшая практика для создания пакета - запуск и доступ к другим модулям из модуля, такого как main_module.py в каталоге самого высокого уровня.

Эта структура демонстрирует, что вы можете использовать подпакет, родительский пакет или пакеты и модули того же уровня и получать к ним доступ с помощью файла каталога верхнего уровня. main_module.py.

Создайте и запустите эти файлы и папки для тестирования:

 package/
    |
    |----- __init__.py (Empty file)
    |------- main_module.py (Contains: import subpackage_1.module_1)        
    |------- module_0.py (Contains: print('module_0 at parent directory, is imported'))
    |           
    |
    |------- subpackage_1/
    |           |
    |           |----- __init__.py (Empty file)
    |           |----- module_1.py (Contains: print('importing other modules from module_1...')
    |           |                             import module_0
    |           |                             import subpackage_2.module_2
    |           |                             import subpackage_1.sub_subpackage_3.module_3)
    |           |----- photo.png
    |           |
    |           |
    |           |----- sub_subpackage_3/
    |                        |
    |                        |----- __init__.py (Empty file)
    |                        |----- module_3.py (Contains: print('module_3 at sub directory, is imported')) 
    |
    |------- subpackage_2/
    |           |
    |           |----- __init__.py (Empty file)
    |           |----- module_2.py (Contains: print('module_2 at same level directory, is imported'))

Теперь беги main_module.py

выход

>>>'importing other modules from module_1...'
   'module_0 at parent directory, is imported'
   'module_2 at same level directory, is imported'
   'module_3 at sub directory, is imported'

При открытии изображений и файлов обратите внимание:

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

Предположим, вы бежите module_0.py и ты хочешь открыть photo.png внутри module_1.py.

что module_1.py должен содержать:

Верный:

image_path = 'subpackage_1/photo.png'
cv2.imread(image_path)

Неправильно:

image_path = 'photo.png'
cv2.imread(image_path)

хотя module_1.py а также photo.png находятся в том же каталоге.

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

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

import some_file

В моем случае у меня был класс для импорта. Мой файл выглядел так:

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

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

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

Я совершенно особенный: я использую 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)

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

Версия 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")

Удачного кодирования!

Ого, не ожидала, что потрачу на это столько времени. Для меня работало следующее:

ОС : Виндовс 10

Питон : v3.10.0

Примечание . Поскольку у меня Python v3.10.0, я не использую __init__.pyфайлы, которые все равно не работали для меня.

      application
├── app
│   └── folder
│       └── file.py
└── app2
    └── some_folder
        └── some_file.py

Первое решение WY Hsu сработало для меня. Я разместил его с абсолютной ссылкой на файл для ясности:

      import sys
sys.path.insert(1, 'C:\\Users\\<Your Username>\\application')
import app2.some_folder.some_file

some_file.hello_world()

Альтернативное решение . Однако это также сработало для меня:

      import sys
sys.path.append( '.' )
import app2.some_folder.some_file

some_file.hello_world()

Хотя, я не понимаю, почему это работает. Я думал, что точка - это ссылка на текущий каталог. Однако при печати путей к текущей папке текущий каталог уже указан вверху:

      for path in sys.path:
    print(path)

Надеюсь, кто-то может дать ясность относительно того, почему это работает в комментариях. Тем не менее, я также надеюсь, что это поможет кому-то.

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

from <MySubFolder> import <MyFile>

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

Если цель загрузки модуля по определенному пути - помочь вам при разработке пользовательского модуля, вы можете создать символическую ссылку в той же папке тестового сценария, которая указывает на корень пользовательского модуля. Эта ссылка на модуль будет иметь приоритет над любыми другими модулями, установленными с таким же именем, для любого сценария, выполняемого в этой папке.

Я проверил это на Linux, но оно должно работать в любой современной ОС, которая поддерживает символические ссылки.

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

Мое решение для людей. у кого есть все необходимое __init__.pyв пакете, но импорт по-прежнему не работает.

      import sys
import os
sys.path.insert(0, os.getcwd())

import application.app.folder.file as file

На случай, если кто-то все еще ищет решение. Это сработало для меня.

Python добавляет папку, содержащую запускаемый вами скрипт, в PYTHONPATH, поэтому, если вы запустите

python application/app2/some_folder/some_file.py

В путь добавляется только папка application/app2/some_folder (а не базовый каталог, в котором вы выполняете команду). Вместо этого запустите свой файл как модуль и добавьте __init__.py в свой каталог some_folder.

python -m application.app2.some_folder.some_file

Это добавит базовый каталог к ​​пути python, и тогда классы будут доступны через не относительный импорт.

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

Приведенный ниже код импортирует сценарий Python, указанный в его пути, независимо от того, где он находится, способом сохранения версии Python:

def import_module_by_path(path):
    name = os.path.splitext(os.path.basename(path))[0]
    if sys.version_info[0] == 2:   
        # Python 2
        import imp
        return imp.load_source(name, path)
    elif sys.version_info[:2] <= (3, 4):  
        # Python 3, version <= 3.4
        from importlib.machinery import SourceFileLoader
        return SourceFileLoader(name, path).load_module()
    else:                            
        # Python 3, after 3.4
        import importlib.util
        spec = importlib.util.spec_from_file_location(name, path)
        mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(mod)
        return mod

Этот код написан не мной; Я нашел его в базе кода psutils, в psutils.test.__init__.py(строка 1042; постоянная ссылка на последнюю фиксацию от 09.10.2020).

Пример использования:

script = "/home/username/Documents/some_script.py"
some_module = import_module_by_path(script)
print(some_module.foo())

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

Эта проблема может быть связана с Pycharm

У меня была такая же проблема при использовании Pycharm. У меня была эта структура проекта

      skylake\
   backend\
      apps\
          example.py
      configuration\
          settings.py
   frontend\
      ...some_stuff

и код from configuration import settingsв example.py возникла ошибка импорта

проблема заключалась в том, что когда я открыл Pycharm, он посчитал, что skylake является корневым путем, и запустил этот код

      sys.path.extend(['D:\\projects\\skylake', 'D:/projects/skylake'])

Чтобы исправить это, я просто пометил внутренний каталог как исходный корень.

И это решило мою проблему

Вы можете использоватьpipкоманда. Вы должны создать файл с именемsetup.pyв корне каталога проекта, который содержит следующее:

      from setuptools import find_packages, setup

setup(
    name='src',
    packages=find_packages(),
    version='0.1.0',
    description='my_project',
    author='author',
    license='MIT',
)

После этого введитеpip install -e .находясь в корневом каталоге вашего проекта. Это позволит вызывать все каталоги с их именем в качестве модуля. Например, если ваш корневой каталог содержит подкаталоги иmodule2, внутри каждого из которых есть сценарии, вы сможете получить к ним доступ из любых подкаталогов с помощью следующей команды:module1:

import module1.script1 as script1

Обычно я создаю символическую ссылку на модуль, который хочу импортировать. Символьная ссылка гарантирует, что интерпретатор Python может найти модуль внутри текущего каталога (сценарий, в который вы импортируете другой модуль); позже, когда ваша работа закончится, вы можете удалить символическую ссылку. Кроме того, вы должны игнорировать символические ссылки в.gitignore, чтобы случайно не зафиксировать модули с символическими ссылками в своем репо. Такой подход позволяет вам даже успешно работать с модулями, расположенными параллельно выполняемому вами сценарию.

ln -s ~/path/to/original/module/my_module ~/symlink/inside/the/destination/directory/my_module
Другие вопросы по тегам