Come importare un modulo dato il percorso completo?


1142

Come posso caricare un modulo Python dato il suo percorso completo? Nota che il file può trovarsi ovunque nel filesystem, in quanto è un'opzione di configurazione.


21
Domanda piacevole e semplice - e risposte utili ma mi fanno domandare cosa sia successo con il mantra in pitone "Esiste un modo ovvio " per farlo. Non sembra niente di simile a una risposta semplice o ovvia. Sembra ridicolmente confuso e dipendente dalla versione per un'operazione così fondamentale (e sembra e più gonfio nelle versioni più recenti ..).
inger il

Risposte:


1283

Per Python 3.5+ utilizzare:

import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()

Per Python 3.3 e 3.4 usare:

from importlib.machinery import SourceFileLoader

foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()

(Anche se questo è stato deprecato in Python 3.4.)

Per Python 2 usare:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Esistono funzioni di convenienza equivalenti per i file e le DLL Python compilati.

Vedi anche http://bugs.python.org/issue21436 .


53
Se conoscessi lo spazio dei nomi - 'module.name' - lo userei già __import__.
Sridhar Ratnakumar,

62
@SridharRatnakumar il valore del primo argomento imp.load_sourceimposta solo .__name__il modulo restituito. non ha effetto sul caricamento.
Dan D.

17
@DanD. - il primo argomento di imp.load_source()determina la chiave della nuova voce creata nel sys.modulesdizionario, quindi il primo argomento influisce sul caricamento.
Brandon Rhodes,

9
Il impmodulo è obsoleto dalla versione 3.4: il imppacchetto è in attesa di svalutazione a favore di importlib.
Chiel ten Brinke l'

9
@AXO e molto altro al punto ci si chiede perché qualcosa di così semplice ed essenziale come questo debba essere così complicato. Non è in molte altre lingue.
roccioso,

423

Il vantaggio di aggiungere un percorso a sys.path (oltre a usare imp) è che semplifica le cose quando si importa più di un modulo da un singolo pacchetto. Per esempio:

import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')

from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch

13
Come possiamo usare sys.path.appendper puntare a un singolo file Python invece che a una directory?
Phani,

28
:-) Forse la tua domanda sarebbe più adatta come domanda StackOverflow, non come un commento a una risposta.
Daryl Spitzer il

3
Il percorso di Python può contenere archivi zip, "uova" (un tipo complesso di archivi zip), ecc. I moduli possono essere importati da essi. Quindi gli elementi del percorso sono effettivamente contenitori di file, ma non sono necessariamente directory.
alexis,

12
Fai attenzione al fatto che Python memorizza nella cache le istruzioni di importazione. Nel raro caso in cui due cartelle diverse condividano un singolo nome di classe (classX), l'approccio di aggiungere un percorso a sys.path, importare classX, rimuovere il percorso e ripetere per i percorsi rimanenti non funzionerà. Python caricherà sempre la classe dal primo percorso dalla sua cache. Nel mio caso ho mirato a creare un sistema di plugin in cui tutti i plugin implementano una specifica classeX. Ho finito per usare SourceFileLoader , nota che la sua deprecazione è controversa .
ComFreek,

3
Si noti che questo approccio consente al modulo importato di importare altri moduli dalla stessa directory, cosa che fanno spesso i moduli, mentre l'approccio della risposta accettata non lo fa (almeno su 3.7). importlib.import_module(mod_name)può essere usato al posto dell'importazione esplicita qui se il nome del modulo non è noto in fase di runtime, aggiungerei un sys.path.pop()alla fine, tuttavia, supponendo che il codice importato non tenti di importare più moduli mentre viene utilizzato.
Eli_B,

43

Se il modulo di livello superiore non è un file ma è impacchettato come directory con __init__.py, la soluzione accettata funziona quasi, ma non del tutto. In Python 3.5+ è necessario il seguente codice (notare la riga aggiunta che inizia con 'sys.modules'):

MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
import importlib
import sys
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module 
spec.loader.exec_module(module)

Senza questa riga, quando exec_module viene eseguito, cerca di associare le importazioni relative nel tuo livello superiore __init__.py al nome del modulo di livello superiore - in questo caso "mymodule". Ma "mymodule" non è stato ancora caricato, quindi verrà visualizzato l'errore "SystemError: modulo genitore 'mymodule' non caricato, impossibile eseguire l'importazione relativa". Quindi è necessario associare il nome prima di caricarlo. La ragione di ciò è il fondamentale invariante del relativo sistema di importazione: "La tenuta invariante è che se hai sys.modules ['spam'] e sys.modules ['spam.foo'] (come faresti dopo l'importazione sopra ), quest'ultimo deve apparire come l'attributo foo del primo ", come discusso qui .


Molte grazie! Questo metodo consente le importazioni relative tra i sottomoduli. Grande!
Tebanep,

Questa risposta corrisponde alla documentazione qui: docs.python.org/3/library/… .
Tim Ludwinski,

1
ma che cos'è mymodule?
Gulzar,

@Gulzar, è qualunque sia il nome che vorresti dare al tuo modulo, in modo tale da poterlo fare in seguito: "da mymodule import myclass"
Idodo

Quindi ... /path/to/your/module/è in realtà /path/to/your/PACKAGE/? e mymoduleintendi myfile.py?
Gulzar,

37

Per importare il tuo modulo, devi aggiungere la sua directory alla variabile d'ambiente, temporaneamente o permanentemente.

Temporaneamente

import sys
sys.path.append("/path/to/my/modules/")
import my_module

Permanentemente

Aggiungendo la seguente riga al tuo .bashrcfile (in Linux) ed esegui source ~/.bashrcnel terminale:

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"

Credito / Fonte: saarrrr , un'altra domanda di scambio di stack


3
Questa soluzione "temporanea" è un'ottima risposta se si desidera creare un progetto in un notebook jupyter altrove.
fordy

Ma ... è pericoloso manomettere il percorso
Shai Alon,

@ShaiAlon Stai aggiungendo percorsi, quindi nessun pericolo se non quando trasferisci i codici da un computer all'altro, i percorsi potrebbero incasinarsi. Quindi, per lo sviluppo di pacchetti, importare solo pacchetti locali. Inoltre, i nomi dei pacchetti dovrebbero essere univoci. Se sei preoccupato, usa la soluzione temporanea.
Miladiouss,

28

Sembra che tu non voglia importare in modo specifico il file di configurazione (che ha molti effetti collaterali e ulteriori complicazioni), vuoi solo eseguirlo ed essere in grado di accedere allo spazio dei nomi risultante. La libreria standard fornisce un'API specifica per questo sotto forma di runpy.run_path :

from runpy import run_path
settings = run_path("/path/to/file.py")

Tale interfaccia è disponibile in Python 2.7 e Python 3.2+


Mi piace questo metodo ma quando ottengo il risultato di run_path è un dizionario a cui non riesco ad accedere?
Stephen Ellwood,

Cosa intendi con "impossibile accedere"? Non è possibile importare da esso (è per questo che questa è solo una buona opzione quando l'accesso import-stile non è effettivamente necessaria), ma i contenuti devono essere disponibili tramite l'API dict regolare ( result[name], result.get('name', default_value), ecc)
ncoghlan

Questa risposta è sottovalutata. È molto breve e semplice! Ancora meglio, se hai bisogno di un adeguato spazio dei nomi dei moduli, puoi fare qualcosa del genere from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo

20

Puoi anche fare qualcosa del genere e aggiungere la directory in cui si trova il file di configurazione nel percorso di caricamento di Python, quindi eseguire un'importazione normale, supponendo che tu conosca in anticipo il nome del file, in questo caso "config".

Disordinato, ma funziona.

configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config

Questo non è dinamico.
Shai Alon,

Ho provato: config_file = 'setup-for-chats', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_file))), import config_file >> "ImportError: nessun modulo chiamato config_file"
Shai Alon,

17

Puoi usare il

load_source(module_name, path_to_file) 

metodo dal modulo imp .


... e imp.load_dynamic(module_name, path_to_file)per DLL
HEKTO

34
avvisa che imp è deprecato ora.
t1m0,

13
def import_file(full_path_to_module):
    try:
        import os
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)
        save_cwd = os.getcwd()
        os.chdir(module_dir)
        module_obj = __import__(module_name)
        module_obj.__file__ = full_path_to_module
        globals()[module_name] = module_obj
        os.chdir(save_cwd)
    except:
        raise ImportError

import_file('/home/somebody/somemodule.py')

37
Perché scrivere 14 righe di codice errato quando questo è già indirizzato dalla libreria standard? Non hai eseguito errori nel controllo del formato o del contenuto di full_path_to_module o delle operazioni os.whatever; e l'uso di una except:clausola generale è raramente una buona idea.
Chris Johnson,

Dovresti usare più "try-finally" s qui. Ad esempiosave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
Kay - SE è cattivo il

11
@ChrisJohnson this is already addressed by the standard librarysì, ma Python ha la brutta abitudine di non essere retrocompatibile ... poiché la risposta selezionata dice che ci sono 2 modi diversi prima e dopo 3.3. In tal caso, preferirei scrivere la mia funzione universale piuttosto che controllare la versione al volo. E sì, forse questo codice non è protetto troppo bene dagli errori, ma mostra un'idea (che è os.chdir (), non ci ho pensato), in base al quale posso scrivere un codice migliore. Quindi +1.
Sushi271,

13

Ecco del codice che funziona in tutte le versioni di Python, dalla 2.7-3.5 e probabilmente anche in altre.

config_file = "/tmp/config.py"
with open(config_file) as f:
    code = compile(f.read(), config_file, 'exec')
    exec(code, globals(), locals())

L'ho provato. Può essere brutto ma finora è l'unico che funziona in tutte le versioni.


1
Questa risposta ha funzionato per me dove load_sourcenon lo è perché importa lo script e fornisce allo script l'accesso ai moduli e ai globali al momento dell'importazione.
Klik,

13

Ho escogitato una versione leggermente modificata della meravigliosa risposta di @ SebastianRittau (per Python> 3.4 credo), che ti permetterà di caricare un file con qualsiasi estensione come modulo usando spec_from_loaderinvece di spec_from_file_location:

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader 

spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)

Il vantaggio di codificare il percorso in modo esplicito SourceFileLoaderè che il macchinario non proverà a capire il tipo di file dall'estensione. Questo significa che puoi caricare qualcosa come un .txtfile usando questo metodo, ma non puoi farlo spec_from_file_locationsenza specificare il caricatore perché .txtnon è in importlib.machinery.SOURCE_SUFFIXES.


13

Intendi caricare o importare?

È possibile manipolare l' sys.pathelenco, specificare il percorso del modulo, quindi importare il modulo. Ad esempio, dato un modulo su:

/foo/bar.py

Potresti fare:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar

1
@Wheat Perché sys.path [0: 0] invece di sys.path [0]?
user618677

5
B / c sys.path [0] = xy sovrascrive la prima voce del percorso mentre path [0: 0] = xy è equivalente a path.insert (0, xy)
dom0

2
hm il path.insert ha funzionato per me ma il trucco [0: 0] non ha funzionato.
jsh

11
sys.path[0:0] = ['/foo']
Kevin Edwards,

6
Explicit is better than implicit.Quindi perché non sys.path.insert(0, ...)invece di sys.path[0:0]?
winklerrr

8

Credo che tu possa usare imp.find_module()e imp.load_module()caricare il modulo specificato. Dovrai dividere il nome del modulo fuori dal percorso, ad esempio se desideri caricare /home/mypath/mymodule.pydevi fare:

imp.find_module('mymodule', '/home/mypath/')

... ma questo dovrebbe portare a termine il lavoro.


6

È possibile utilizzare il pkgutilmodulo (in particolare il walk_packagesmetodo) per ottenere un elenco dei pacchetti nella directory corrente. Da lì è banale usare i importlibmacchinari per importare i moduli desiderati:

import pkgutil
import importlib

packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
    mod = importlib.import_module(name)
    # do whatever you want with module now, it's been imported!

5

Crea il modulo python test.py

import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3

Crea il modulo python test_check.py

from test import Client1
from test import Client2
from test import test3

Possiamo importare il modulo importato dal modulo.


4

Questa area di Python 3.4 sembra essere estremamente tortuosa da capire! Tuttavia, con un po 'di hacking utilizzando il codice di Chris Calloway come inizio sono riuscito a far funzionare qualcosa. Ecco la funzione di base.

def import_module_from_file(full_path_to_module):
    """
    Import a module given the full path/filename of the .py file

    Python 3.4

    """

    module = None

    try:

        # Get module name and path from full path
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)

        # Get module "spec" from filename
        spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)

        module = spec.loader.load_module()

    except Exception as ec:
        # Simple error printing
        # Insert "sophisticated" stuff here
        print(ec)

    finally:
        return module

Questo sembra usare moduli non deprecati da Python 3.4. Non pretendo di capire il perché, ma sembra funzionare all'interno di un programma. Ho scoperto che la soluzione di Chris funzionava dalla riga di comando ma non dall'interno di un programma.


4

Non sto dicendo che sia meglio, ma per completezza, ho voluto suggerire che la execfunzione, disponibile sia in Python 2 che in 3. execconsente di eseguire codice arbitrario in ambito globale o in ambito interno, fornito come dizionario.

Ad esempio, se si dispone di un modulo memorizzato in "/path/to/module"con la funzione foo(), è possibile eseguirlo nel modo seguente:

module = dict()
with open("/path/to/module") as f:
    exec(f.read(), module)
module['foo']()

Ciò rende un po 'più esplicito il caricamento dinamico del codice e garantisce ulteriore potenza, ad esempio la possibilità di fornire built-in personalizzati.

E se avere accesso attraverso gli attributi, anziché le chiavi è importante per te, puoi progettare una classe dict personalizzata per i globi, che fornisca tale accesso, ad esempio:

class MyModuleClass(dict):
    def __getattr__(self, name):
        return self.__getitem__(name)

4

Per importare un modulo da un determinato nome file, è possibile estendere temporaneamente il percorso e ripristinare il percorso di sistema nel riferimento del blocco finally :

filename = "directory/module.py"

directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]

path = list(sys.path)
sys.path.insert(0, directory)
try:
    module = __import__(module_name)
finally:
    sys.path[:] = path # restore

4

Se disponiamo di script nello stesso progetto ma in directory diverse, possiamo risolvere questo problema con il seguente metodo.

In questa situazione utils.pyè dentrosrc/main/util/

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

import src.main.util.utils
#or
from src.main.util.utils import json_converter # json_converter is example method

il più semplice IMO
Ardhi

3

Questo dovrebbe funzionare

path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
    basename = os.path.basename(infile)
    basename_without_extension = basename[:-3]

    # http://docs.python.org/library/imp.html?highlight=imp#module-imp
    imp.load_source(basename_without_extension, infile)

4
Un modo più generale per tagliare l'out estensione è: name, ext = os.path.splitext(os.path.basename(infile)). Il tuo metodo funziona perché la precedente limitazione all'estensione .py. Inoltre, dovresti probabilmente importare il modulo in qualche voce variabile / dizionario.
ReneSac,

2

Ho creato un pacchetto che utilizza impper te. Lo chiamo import_fileed è così che viene utilizzato:

>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')

Puoi ottenerlo su:

http://pypi.python.org/pypi/import_file

o a

http://code.google.com/p/import-file/


1
os.chdir? (caratteri minimi per l'approvazione del commento).
ychaouche,

Ho passato tutto il giorno a risolvere un bug di importazione in un exe generato da pyinstaller. Alla fine questa è l'unica cosa che ha funzionato per me. Grazie mille per averlo fatto!
frakman1,

2

Importa i moduli del pacchetto in fase di esecuzione (ricetta Python)

http://code.activestate.com/recipes/223972/

###################
##                #
## classloader.py #
##                #
###################

import sys, types

def _get_mod(modulePath):
    try:
        aMod = sys.modules[modulePath]
        if not isinstance(aMod, types.ModuleType):
            raise KeyError
    except KeyError:
        # The last [''] is very important!
        aMod = __import__(modulePath, globals(), locals(), [''])
        sys.modules[modulePath] = aMod
    return aMod

def _get_func(fullFuncName):
    """Retrieve a function object from a full dotted-package name."""

    # Parse out the path, module, and function
    lastDot = fullFuncName.rfind(u".")
    funcName = fullFuncName[lastDot + 1:]
    modPath = fullFuncName[:lastDot]

    aMod = _get_mod(modPath)
    aFunc = getattr(aMod, funcName)

    # Assert that the function is a *callable* attribute.
    assert callable(aFunc), u"%s is not callable." % fullFuncName

    # Return a reference to the function itself,
    # not the results of the function.
    return aFunc

def _get_class(fullClassName, parentClass=None):
    """Load a module and retrieve a class (NOT an instance).

    If the parentClass is supplied, className must be of parentClass
    or a subclass of parentClass (or None is returned).
    """
    aClass = _get_func(fullClassName)

    # Assert that the class is a subclass of parentClass.
    if parentClass is not None:
        if not issubclass(aClass, parentClass):
            raise TypeError(u"%s is not a subclass of %s" %
                            (fullClassName, parentClass))

    # Return a reference to the class itself, not an instantiated object.
    return aClass


######################
##       Usage      ##
######################

class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass

def storage_object(aFullClassName, allOptions={}):
    aStoreClass = _get_class(aFullClassName, StorageManager)
    return aStoreClass(allOptions)

2

In Linux, l'aggiunta di un collegamento simbolico nella directory in cui si trova lo script python funziona.

vale a dire:

ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py

python creerà /absolute/path/to/script/module.pyce lo aggiornerà se cambi il contenuto di/absolute/path/to/module/module.py

quindi includere quanto segue in mypythonscript.py

from module import *

1
Questo è l'hack che ho usato e mi ha causato alcuni problemi. Uno dei più dolorosi è stato il fatto che IDEA ha un problema in cui non rileva il codice alterato all'interno del collegamento, ma tenta comunque di salvare ciò che pensa ci sia. Una condizione di gara in cui l'ultimo da salvare è ciò che rimane ... Ho perso una discreta quantità di lavoro a causa di questo.
Gripp,

@Gripp non sono sicuro di capire il tuo problema, ma modifico frequentemente (quasi esclusivamente) i miei script su un server remoto dal mio desktop tramite SFTP con un client come CyberDuck, e in quel caso è una cattiva idea provare e modifica il file con collegamento simbolico, invece è molto più sicuro modificare il file originale. È possibile rilevare alcuni di questi problemi utilizzando gite controllando il proprio git statusper verificare che le modifiche apportate allo script stiano effettivamente tornando al documento di origine e non si perdano nell'etere.
user5359531

2

Ho scritto la mia funzione di importazione globale e portatile, basata sul importlibmodulo, per:

  • Essere in grado di importare entrambi i moduli come sottomodulo e di importare il contenuto di un modulo in un modulo principale (o in un globale se non ha un modulo principale).
  • Essere in grado di importare moduli con un punto caratteri in un nome file.
  • Essere in grado di importare moduli con qualsiasi estensione.
  • Essere in grado di utilizzare un nome autonomo per un sottomodulo invece di un nome file senza estensione, che è di default.
  • Essere in grado di definire l'ordine di importazione in base al modulo importato in precedenza anziché dipendere da sys.patho su qualsiasi memoria del percorso di ricerca.

La struttura di directory degli esempi:

<root>
 |
 +- test.py
 |
 +- testlib.py
 |
 +- /std1
 |   |
 |   +- testlib.std1.py
 |
 +- /std2
 |   |
 |   +- testlib.std2.py
 |
 +- /std3
     |
     +- testlib.std3.py

Dipendenza e ordine dell'inclusione:

test.py
  -> testlib.py
    -> testlib.std1.py
      -> testlib.std2.py
    -> testlib.std3.py 

Implementazione:

Archivio delle ultime modifiche: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

test.py :

import os, sys, inspect, copy

SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("test::SOURCE_FILE: ", SOURCE_FILE)

# portable import to the global space
sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
import tacklelib as tkl

tkl.tkl_init(tkl)

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

tkl_import_module(SOURCE_DIR, 'testlib.py')

print(globals().keys())

testlib.base_test()
testlib.testlib_std1.std1_test()
testlib.testlib_std1.testlib_std2.std2_test()
#testlib.testlib.std3.std3_test()                             # does not reachable directly ...
getattr(globals()['testlib'], 'testlib.std3').std3_test()     # ... but reachable through the `globals` + `getattr`

tkl_import_module(SOURCE_DIR, 'testlib.py', '.')

print(globals().keys())

base_test()
testlib_std1.std1_test()
testlib_std1.testlib_std2.std2_test()
#testlib.std3.std3_test()                                     # does not reachable directly ...
globals()['testlib.std3'].std3_test()                         # ... but reachable through the `globals` + `getattr`

testlib.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')

# SOURCE_DIR is restored here
print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')

print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)

def base_test():
  print('base_test')

testlib.std1.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')

def std1_test():
  print('std1_test')

testlib.std2.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)

def std2_test():
  print('std2_test')

testlib.std3.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)

def std3_test():
  print('std3_test')

Uscita ( 3.7.4):

test::SOURCE_FILE:  <root>/test01/test.py
import : <root>/test01/testlib.py as testlib -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
base_test
std1_test
std2_test
std3_test
import : <root>/test01/testlib.py as . -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
base_test
std1_test
std2_test
std3_test

Testato in Python 3.7.4, 3.2.5,2.7.16

Pro :

  • Può importare entrambi i moduli come sottomodulo e può importare il contenuto di un modulo in un modulo principale (o in un globale se non ha un modulo principale).
  • Può importare moduli con punti in un nome file.
  • Può importare qualsiasi modulo di estensione da qualsiasi modulo di estensione.
  • Può utilizzare un nome autonomo per un sottomodulo anziché un nome file senza estensione, che è di default (ad esempio, testlib.std.pycome testlib, testlib.blabla.pycome testlib_blablae così via).
  • Non dipende da un sys.patho da un archivio di percorsi di ricerca.
  • Non richiede di salvare / ripristinare variabili globali come SOURCE_FILEe SOURCE_DIRtra le chiamate a tkl_import_module.
  • [per 3.4.xe successive] Può mescolare gli spazi dei nomi del modulo in tkl_import_modulechiamate nidificate (es: named->local->namedo local->named->localcosì via).
  • [per 3.4.xe successivi] Può esportare automaticamente variabili / funzioni / classi globali da dove viene dichiarato a tutti i moduli figli importati attraverso tkl_import_module(attraverso la tkl_declare_globalfunzione).

Contro :

  • [per 3.3.xe inferiore] Richiesta di dichiarazione tkl_import_modulein tutti i moduli che chiama a tkl_import_module(duplicazione del codice)

Aggiornamento 1,2 (solo per 3.4.xe successivi):

In Python 3.4 e versioni successive è possibile ignorare il requisito di dichiarare tkl_import_modulein ciascun modulo dichiarando tkl_import_modulein un modulo di livello superiore e la funzione si inietterebbe da sola in tutti i moduli figlio in una singola chiamata (è una specie di importazione self-deploy).

Aggiornamento 3 :

Aggiunta funzione tkl_source_modulecome analogica per bash sourcecon supporto per la protezione dell'esecuzione al momento dell'importazione (implementata tramite l'unione del modulo anziché l'importazione).

Aggiornamento 4 :

Aggiunta funzione tkl_declare_globalper esportare automaticamente una variabile globale del modulo in tutti i moduli figlio in cui una variabile globale del modulo non è visibile perché non fa parte di un modulo figlio.

Aggiornamento 5 :

Tutte le funzioni sono state spostate nella libreria tacklelib, vedere il link sopra.


2

C'è un pacchetto dedicato a questo in particolare:

from thesmuggler import smuggle

# À la `import weapons`
weapons = smuggle('weapons.py')

# À la `from contraband import drugs, alcohol`
drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')

# À la `from contraband import drugs as dope, alcohol as booze`
dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')

È testato su tutte le versioni di Python (anche Jython e PyPy), ma potrebbe essere eccessivo a seconda delle dimensioni del progetto.


1

Aggiungendo questo all'elenco delle risposte in quanto non riuscivo a trovare nulla che funzionasse. Ciò consentirà l'importazione di moduli python compilati (pyd) in 3.4:

import sys
import importlib.machinery

def load_module(name, filename):
    # If the Loader finds the module name in this list it will use
    # module_name.__file__ instead so we need to delete it here
    if name in sys.modules:
        del sys.modules[name]
    loader = importlib.machinery.ExtensionFileLoader(name, filename)
    module = loader.load_module()
    locals()[name] = module
    globals()[name] = module

load_module('something', r'C:\Path\To\something.pyd')
something.do_something()

1

modo abbastanza semplice: supponiamo di voler importare il file con relativo percorso ../../MyLibs/pyfunc.py


libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf

Ma se lo fai senza una guardia puoi finalmente ottenere un percorso molto lungo


1

Una soluzione semplice che utilizza al importlibposto del imppacchetto (testato per Python 2.7, anche se dovrebbe funzionare anche per Python 3):

import importlib

dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")

Ora puoi utilizzare direttamente lo spazio dei nomi del modulo importato, in questo modo:

a = module.myvar
b = module.myfunc(a)

Il vantaggio di questa soluzione è che non abbiamo nemmeno bisogno di conoscere il nome effettivo del modulo che vorremmo importare , al fine di utilizzarlo nel nostro codice. Ciò è utile, ad esempio nel caso in cui il percorso del modulo sia un argomento configurabile.


In questo modo si sta modificando il sys.path, che non si adatta a tutti i casi d'uso.
bgusach,

@bgusach Questo può essere vero, ma è anche desiderabile in alcuni casi (l'aggiunta di un percorso a sys.path semplifica le cose quando si importa più di un modulo da un singolo pacchetto). Ad ogni modo, se ciò non è desiderabile, si può fare subito doposys.path.pop()
Atassia il

0

Questa risposta è un supplemento alla risposta di Sebastian Rittau che risponde al commento: "ma cosa succede se non si dispone del nome del modulo?" Questo è un modo rapido e sporco per ottenere il nome del modulo Python probabile dato un nome di file - sale solo fino a quando non trova una directory senza un __init__.pyfile e poi lo trasforma in un nome file. Per Python 3.4+ (usa pathlib), il che ha senso dato che le persone Py2 possono usare "imp" o altri modi di fare le relative importazioni:

import pathlib

def likely_python_module(filename):
    '''
    Given a filename or Path, return the "likely" python module name.  That is, iterate
    the parent directories until it doesn't contain an __init__.py file.

    :rtype: str
    '''
    p = pathlib.Path(filename).resolve()
    paths = []
    if p.name != '__init__.py':
        paths.append(p.stem)
    while True:
        p = p.parent
        if not p:
            break
        if not p.is_dir():
            break

        inits = [f for f in p.iterdir() if f.name == '__init__.py']
        if not inits:
            break

        paths.append(p.stem)

    return '.'.join(reversed(paths))

Ci sono certamente possibilità di miglioramento e i __init__.pyfile opzionali potrebbero richiedere altre modifiche, ma se hai __init__.pyin generale, questo fa il trucco.


-1

Il modo migliore, penso, è dalla documentazione ufficiale ( 29.1. Imp - Accedi agli import interni ):

import imp
import sys

def __import__(name, globals=None, locals=None, fromlist=None):
    # Fast path: see if the module has already been imported.
    try:
        return sys.modules[name]
    except KeyError:
        pass

    # If any of the following calls raises an exception,
    # there's a problem we can't handle -- let the caller handle it.

    fp, pathname, description = imp.find_module(name)

    try:
        return imp.load_module(name, fp, pathname, description)
    finally:
        # Since we may exit via an exception, close fp explicitly.
        if fp:
            fp.close()

1
Questa soluzione non consente di fornire il percorso, che è ciò che la domanda richiede.
Micah Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.