Bestanden importeren uit een andere map

Ik heb de volgende mappenstructuur.

application
+-- app
¦   L-- folder
¦       L-- file.py
L-- app2
    L-- some_folder
        L-- some_file.py

Ik wil enkele functies importeren uit file.py in some_file.py.

Ik heb het geprobeerd

from application.app.folder.file import func_name

en enkele andere verschillende pogingen, maar tot nu toe is het me niet gelukt om correct te importeren. Hoe kan ik dit doen?


Antwoord 1, autoriteit 100%

Opmerking: dit antwoord was bedoeld voor een zeer specifieke vraag. Voor de meeste programmeurs die hier vanuit een zoekmachine komen, is dit niet het antwoord dat u zoekt. Normaal gesproken zou u uw bestanden in pakketten structureren (zie andere antwoorden) in plaats van het zoekpad aan te passen.


Standaard kun je dat niet. Bij het importeren van een bestand doorzoekt Python alleen de map waar het entrypointscript vandaan komt en sys.path die locaties bevat zoals de pakketinstallatiemap (het is eigenlijk een beetje ingewikkelder dan dit, maar dit dekt de meeste gevallen).

U kunt echter tijdens runtime aan het Python-pad toevoegen:

# 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

Antwoord 2, autoriteit 53%

Niets mis met:

from application.app.folder.file import func_name

Zorg er wel voor dat de folder ook een __init__.py bevat, zodat deze als pakket kan worden opgenomen. Ik weet niet zeker waarom de andere antwoorden over PYTHONPATH gaan.


Antwoord 3, autoriteit 8%

Als modules zich op parallelle locaties bevinden, zoals in de vraag:

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

Deze afkorting maakt de ene module zichtbaar voor de andere:

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

Antwoord 4, autoriteit 5%

Eerst sys importeren in name-file.py

 import sys

Tweede voeg het mappad toe in name-file.py

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

Derde Maak een leeg bestand met de naam __ init __.py in je submap (dit vertelt Python dat het een pakket is)

  • naam-bestand.py
  • naam-pakket
    • __ init __.py
    • naam-module.py

Vierde importeer de module in de map in name-file.py

from name-package import name-module

Antwoord 5, autoriteit 4%

Ik denk dat een ad-hoc manier zou zijn om de omgevingsvariabele te gebruiken PYTHONPATH zoals beschreven in de documentatie: Python2, Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH
# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%

Antwoord 6, autoriteit 3%

Uw probleem is dat Python in de Python-directory naar dit bestand zoekt en het niet vindt. Je moet specificeren dat je het hebt over de map waarin je je bevindt en niet over de Python-map.

Om dit te doen, verander je dit:

from application.app.folder.file import func_name

naar dit:

from .application.app.folder.file import func_name

Door de punt toe te voegen, zeg je dat je in deze map moet zoeken naar de applicatiemap in plaats van in de Python-directory.


Antwoord 7, autoriteit 3%

De antwoorden hier zijn niet duidelijk, dit is getest op Python 3.6

Met deze mappenstructuur:

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

Waar myfile.py de inhoud heeft:

def myfunc():
    print('hello')

Het importstatement in main.py is:

from myfolder.myfile import myfunc
myfunc()

en dit zal hallo afdrukken.


Antwoord 8, autoriteit 2%

Voor zover ik weet, zal het toevoegen van een __init__.py-bestand rechtstreeks in de map met de functies die u wilt importeren de klus klaren.


Antwoord 9, autoriteit 2%

In Python 3.4 en hoger kunt u rechtstreeks importeren uit een bronbestand (link naar documentatie). Dit is niet de eenvoudigste oplossing, maar voor de volledigheid voeg ik dit antwoord toe.

Hier is een voorbeeld. Eerst het te importeren bestand, genaamd foo.py:

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

De code die het bovenstaande bestand importeert, sterk geïnspireerd door het voorbeeld in de documentatie:

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

De uitvoer:

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

Houd er rekening mee dat de variabelenaam, de modulenaam en de bestandsnaam niet overeen hoeven te komen. Deze code werkt nog steeds:

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

De uitvoer:

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

Het programmatisch importeren van modules is geïntroduceerd in Python 3.1 en geeft je meer controle over hoe modules worden geïmporteerd. Raadpleeg de documentatie voor meer informatie.


Antwoord 10, autoriteit 2%

Ik stond voor dezelfde uitdaging, vooral bij het importeren van meerdere bestanden, zo heb ik het voor elkaar gekregen.

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

Antwoord 11

Werkte voor mij in python3 op linux

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

Antwoord 12

Probeer de relatieve import van Python:

from ...app.folder.file import func_name

Elke eerste punt is een ander hoger niveau in de hiërarchie, beginnend met de huidige directory.


Problemen? Als dit niet voor u werkt, wordt u waarschijnlijk gebeten door de vele relatieve invoer van Gotcha.
Lees antwoorden en opmerkingen voor meer details:
Hoe “Attempted” op te lossen relatieve import in niet-pakket" zelfs met __init__.py

Hint: gebruik __init__.py op elk directoryniveau. Mogelijk hebt u python -m application.app2.some_folder.some_file nodig (waarbij u .py verlaat) die u uitvoert vanuit de directory op het hoogste niveau of die directory op het hoogste niveau in uw PYTHONPATH heeft. Pfoe!


Antwoord 13

Het gebruik van sys.path.append met een absoluut pad is niet ideaal bij het verplaatsen van de toepassing naar andere omgevingen. Het gebruik van een relatief pad zal niet altijd werken omdat de huidige werkdirectory afhangt van hoe het script werd aangeroepen.

Aangezien de mapstructuur van de applicatie vast is, kunnen we os.path gebruiken om het volledige pad te krijgen van de module die we willen importeren. Als dit bijvoorbeeld de structuur is:

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

En laten we zeggen dat je de mango-module wilt importeren. U kunt het volgende doen in 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

Natuurlijk heb je de variabele mango_dir niet nodig.

Bekijk dit voorbeeld van een interactieve sessie om te begrijpen hoe dit werkt:

>>> 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'
>>> 

En controleer de os.path documentatie.

Het is ook vermeldenswaard dat het omgaan met meerdere mappen gemakkelijker wordt gemaakt bij het gebruik van pakketten, aangezien men gestippelde modulenamen kan gebruiken.


Antwoord 14

Beschouw application als de hoofdmap voor uw python-project, maak een leeg __init__.py-bestand in application, app en folder mappen. Breng vervolgens in uw some_file.py de volgende wijzigingen aan om de definitie van func_name te krijgen:

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

Antwoord 15

Dit werkt voor mij op Windows

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

Antwoord 16

In mijn geval moest ik een klas importeren. Mijn bestand zag er als volgt uit:

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

In mijn hoofdbestand heb ik de code opgenomen via:

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

Antwoord 17

De beste werkwijze voor het maken van een pakket kan zijn dat de andere modules worden uitgevoerd en geopend vanuit een module zoals main_module.py in de directory op het hoogste niveau.

Deze structuur laat zien dat u subpakketten, bovenliggende pakketten of pakketten en modules van hetzelfde niveau kunt gebruiken en openen met behulp van een directorybestand op het hoogste niveau main_module.py.

Maak deze bestanden en mappen aan en voer ze uit om te testen:

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

Voer nu main_module.py

uit

de uitvoer is

>>>'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'

Opmerking bij het openen van afbeeldingen en bestanden:

Als u in een pakketstructuur toegang wilt tot een foto, gebruik dan de absolute directory van de hoogste directory.

Laten we aannemen dat u main_module.py gebruikt en dat u photo.png wilt openen in module_1.py.

wat module_1.py moet bevatten is:

Correct:

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

Fout:

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

hoewel module_1.py en photo.png zich in dezelfde map bevinden.


Antwoord 18

Ik ben best speciaal: ik gebruik Python met Windows!

Ik vul alleen informatie in: voor zowel Windows als Linux werken zowel het relatieve als het absolute pad in sys.path (ik heb relatieve paden nodig omdat ik mijn scripts op de verschillende pc’s en onder verschillende hoofdmappen gebruik ).

En bij gebruik van Windows kunnen zowel \ als / worden gebruikt als scheidingsteken voor bestandsnamen en natuurlijk moet je \ verdubbelen in Python-strings ,
enkele geldige voorbeelden:

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

(opmerking: ik denk dat / handiger is dan \, als het minder ‘Windows-native’ is omdat het Linux-compatibel is en eenvoudiger te schrijven is en kopieer naar Windows verkenner)


Antwoord 19

Ik ben dezelfde vraag meerdere keren tegengekomen, dus ik wil graag mijn oplossing delen.

Python-versie: 3.X

De volgende oplossing is voor iemand die uw toepassing ontwikkelt in Python versie 3.X omdat Python 2 wordt niet meer ondersteund sinds 1 januari 2020.

Projectstructuur

In python 3 heb je __init__.py niet nodig in de submap van je project vanwege de Impliciete naamruimtepakketten. Zie Is init .py niet vereist voor pakketten in Python 3.3+

Project 
+-- main.py
+-- .gitignore
|
+-- a
|   L-- file_a.py
|
L-- b
    L-- file_b.py

Probleemstelling

In file_b.py wil ik een klasse a importeren in file_a.py onder de map a.

Oplossingen

#1 Een snelle maar vuile manier

Zonder het pakket te installeren zoals u momenteel een nieuw project aan het ontwikkelen bent

Gebruik de try catch om te controleren of de fouten. Codevoorbeeld:

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 Installeer je pakket

Zodra je je applicatie hebt geïnstalleerd (in dit bericht is de installatiehandleiding niet inbegrepen)

Je kunt gewoon

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

Veel plezier met coderen!


Antwoord 20

Als het doel van het laden van een module vanaf een specifiek pad is om u te helpen bij het ontwikkelen van een aangepaste module, kunt u een symbolische link maken in dezelfde map van het testscript die naar de hoofdmap van de aangepaste module verwijst. Deze modulereferentie heeft voorrang op alle andere geïnstalleerde modules met dezelfde naam voor elk script dat in die map wordt uitgevoerd.

Ik heb dit getest op Linux, maar het zou moeten werken in elk modern besturingssysteem dat symbolische links ondersteunt.

Een voordeel van deze aanpak is dat je kunt verwijzen naar een module die zich in je eigen lokale werkkopie van de SVC-tak bevindt, wat de ontwikkelingscyclustijd aanzienlijk kan vereenvoudigen en de faalwijzen van het beheer van verschillende versies van de module kan verminderen.

p>


Antwoord 21

In plaats van alleen een import ... te doen, doe je dit:

from <MySubFolder> import <MyFile>

import importeren

MijnBestand bevindt zich in de MijnSubFolder.


Antwoord 22

Ik maak meestal een symbolische link naar de module die ik wil importeren. De symbolische link zorgt ervoor dat de Python-interpreter de module kan lokaliseren in de huidige map (het script waarin je de andere module importeert); later als je werk voorbij is, kun je de symbolische link verwijderen. Je moet ook symlinks in .gitignore negeren, zodat je niet per ongeluk gesymlinkte modules naar je repo zou committen. Met deze aanpak kun je zelfs succesvol werken met modules die parallel staan ​​aan het script dat je uitvoert.

ln -s ~/path/to/original/module/my_module ~/symlink/inside/the/destination/directory/my_module

Antwoord 23

+---root
¦   +---dir_a
¦   ¦   +---file_a.py
¦   ¦   L---file_xx.py
¦   +---dir_b
¦   ¦   +---file_b.py
¦   ¦   L---file_yy.py
¦   +---dir_c
¦   L---dir_n

Je kunt de bovenliggende map toevoegen aan PYTHONPATH, om dat te bereiken, kun je OS-afhankelijk pad gebruiken in het "module zoekpad" die wordt vermeld in sys.path. U kunt dus eenvoudig de bovenliggende map als volgt toevoegen:

# file_b.py
import sys
sys.path.insert(0, '..')
from dir_a.file_a import func_name

Antwoord 24

Ik werkte aan project a waarvan ik wilde dat gebruikers ze installeerden via pip install a met de volgende bestandenlijst:

.
+-- setup.py
+-- MANIFEST.in
L-- a
    +-- __init__.py
    +-- a.py
    L-- b
        +-- __init__.py
        L-- 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

Ik heb de module geïnstalleerd door het volgende uit te voeren vanuit de map met MANIFEST.in:

python setup.py install

Toen kon ik vanaf een totaal andere locatie op mijn bestandssysteem /moustache/armwrestle uitvoeren:

import a
dir(a)

Wat bevestigde dat a.cats inderdaad gelijk was aan 0 en a.b.dogs inderdaad gelijk was aan 1, zoals bedoeld.


Antwoord 25

U kunt importlib gebruiken om modules te importeren waar u een module uit een map wilt importeren met een tekenreeks als volgt:

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

Dit voorbeeld heeft een main.py wat de bovenstaande code is en vervolgens een map met de naam Scripts en dan kun je vanuit deze map alles oproepen wat je nodig hebt door de variabele scriptName te wijzigen. U kunt dan script gebruiken om naar deze module te verwijzen. als ik bijvoorbeeld een functie heb met de naam Hello() in de Snake-module, kun je deze functie als volgt uitvoeren:

script.Hello()

Ik heb dit getest in Python 3.6


Antwoord 26

Ik heb deze problemen een aantal keer gehad. Ik ben vaak op deze zelfde pagina gekomen.
In mijn laatste probleem moest ik de server uitvoeren vanuit een vaste directory, maar bij het debuggen wilde ik vanuit verschillende subdirectories werken.

import sys
sys.insert(1, /path) 

werkte NIET voor mij omdat ik bij verschillende modules verschillende *.csv bestanden moest lezen die allemaal in dezelfde map stonden.

Uiteindelijk was wat voor mij werkte niet pythonisch, denk ik, maar:

Ik heb een if __main__ bovenop de module die ik wilde debuggen gebruikt, die vanuit een ander dan normaal pad wordt uitgevoerd.

Dus:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')

Antwoord 27

Als je meerdere mappen en submappen hebt, kun je altijd elke klasse of module importeren uit de hoofdmap.

Bijvoorbeeld: Boomstructuur van het project

Project 
+-- main.py
+-- .gitignore
|
+-- src
     +----model
     |    L-- user_model.py
     |----controller
          L-- user_controller.py

Als u de klasse "UserModel wilt importeren uit user_model.py in het bestand main.py, kunt u dat doen met:

from src.model.user_model.py import UserModel

U kunt ook dezelfde klasse importeren in het bestand user_controller.py met dezelfde regel:

from src.model.user_model.py import UserModel

Over het algemeen kun je een referentie van de hoofdprojectdirectory geven om klassen en bestanden te importeren in elk python-bestand in de Project-directory.


Antwoord 28

Voor het geval iemand nog op zoek is naar een oplossing. Dit werkte voor mij.

Python voegt de map met het script dat u start toe aan de PYTHONPATH, dus als u het uitvoert

python application/app2/some_folder/some_file.py

Alleen de map application/app2/some_folder wordt toegevoegd aan het pad (niet de basismap waarin u de opdracht uitvoert). Voer in plaats daarvan uw bestand uit als een module en voeg een __init__.py toe aan uw map some_folder.

python -m application.app2.some_folder.some_file

Hiermee wordt de basisdirectory toegevoegd aan het python-pad, waarna klassen toegankelijk zijn via een niet-relatieve import.


Antwoord 29

De onderstaande code importeert het Python-script gegeven door zijn pad, ongeacht waar het zich bevindt, op een Python-versieveilige manier:

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

Ik vond dit in de codebase van psutils, op regel 1042 in psutils.test.__init__.py ( meest recente vastlegging per 09.10.2020).

Gebruiksvoorbeeld:

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

Belangrijk voorbehoud: de module wordt behandeld als het hoogste niveau; alle relatieve importen van bovenliggende pakketten erin zullen mislukken.


Antwoord 30

Je kunt de Python-shell verversen door op f5 te drukken, of ga naar Uitvoeren-> Module uitvoeren. Op deze manier hoeft u de directory niet te wijzigen om iets uit het bestand te lezen. Python zal automatisch de map wijzigen. Maar als je met verschillende bestanden uit een andere map in de Python Shell wilt werken, dan kun je de map in sys wijzigen, zoals Cameron zei eerder.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes