Domanda

Come posso caricare un modulo Python dato il suo percorso completo?Tieni presente che il file può trovarsi ovunque nel filesystem, poiché è un'opzione di configurazione.

È stato utile?

Soluzione

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 utilizzare:

from importlib.machinery import SourceFileLoader

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

(Sebbene questo sia stato deprecato in Python 3.4.)

Per Python 2 utilizzare:

import imp

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

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

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

Altri suggerimenti

Il vantaggio di aggiungere un percorso a sys.path (rispetto all'utilizzo di 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

Puoi anche fare qualcosa del genere e aggiungere la directory in cui si trova il file di configurazione al percorso di caricamento di Python, quindi eseguire semplicemente un'importazione normale, presupponendo 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

Sembra che tu non voglia importare in modo specifico il file di configurazione (che comporta molti effetti collaterali e complicazioni aggiuntive), 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.percorso_esecuzione:

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

Tale interfaccia è disponibile in Python 2.7 e Python 3.2+

Puoi usare il

load_source(module_name, path_to_file) 

metodo da modulo imp.

Se il tuo modulo di livello superiore non è un file ma è impacchettato come una directory con __init__.py, allora 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 viene eseguito exec_module, tenta di associare le importazioni relative nel tuo __init__.py di livello superiore al nome del modulo di livello superiore, in questo caso "mymodule".Ma "mymodule" non è ancora stato caricato, quindi riceverai l'errore "SystemError:Modulo principale 'mymodule' non caricato, impossibile eseguire l'importazione relativa".Quindi è necessario associare il nome prima di caricarlo.La ragione di ciò è l’invariante fondamentale del relativo sistema di importazione:"L'invariante è che se hai sys.modules['spam'] e sys.modules['spam.foo'] (come faresti dopo l'importazione di cui sopra), quest'ultimo deve apparire come attributo foo del primo" come discusso qui.

Ho creato una versione leggermente modificata di La meravigliosa risposta di @SebastianRittau (per Python> 3.4 credo), che ti consentirà di caricare un file con qualsiasi estensione come modulo utilizzando spec_from_loader invece 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 è quello il macchinari non proverà a capire il tipo di file dall'estensione.Ciò significa che puoi caricare qualcosa come a .txt file utilizzando questo metodo, ma non è possibile farlo spec_from_file_location senza specificare il caricatore perché .txt non è dentro importlib.machinery.SOURCE_SUFFIXES.

Ecco del codice che funziona in tutte le versioni di Python, dalla 2.7 alla 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.Potrebbe essere brutto ma finora è l'unico che funziona in tutte le versioni.

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 file .bashrc file (in Linux) ed eseguire source ~/.bashrc nel terminale:

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

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

Intendi caricare o importare?

Puoi manipolare l'elenco sys.path specificando il percorso del tuo modulo, quindi importare il tuo modulo.Ad esempio, dato un modulo in:

/foo/bar.py

Potresti fare:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
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')

Credo che tu possa usare imp.find_module() E imp.load_module() per caricare il modulo specificato.Dovrai dividere il nome del modulo dal percorso, ad es.se volessi caricare /home/mypath/mymodule.py dovresti fare:

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

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

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)

Puoi usare il pkgutil modulo (nello specifico il walk_packages metodo) per ottenere un elenco dei pacchetti nella directory corrente.Da lì è banale usare il file importlib macchinari per importare i moduli che desideri:

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!

Quest'area di Python 3.4 sembra estremamente tortuosa da comprendere!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

Sembra che utilizzi moduli non deprecati da Python 3.4.Non pretendo di capirne il motivo, ma sembra che funzioni dall'interno di un programma.Ho scoperto che la soluzione di Chris funzionava sulla riga di comando ma non dall'interno di un programma.

Non dico che sia migliore, ma per completezza volevo suggerire il exec funzione, disponibile sia in Python 2 che 3.exec consente di eseguire codice arbitrario nell'ambito globale o in un ambito interno, fornito come dizionario.

Ad esempio, se hai un modulo archiviato in "/path/to/module" con la funzione foo(), puoi eseguirlo procedendo come segue:

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

Ciò rende un po' più esplicito il fatto che stai caricando il codice in modo dinamico e ti garantisce potenza aggiuntiva, come la possibilità di fornire built-in personalizzati.

E se per te è importante avere accesso tramite attributi, invece che tramite chiavi, puoi progettare una classe dict personalizzata per i globali, che fornisca tale accesso, ad esempio:

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

Per importare un modulo da un determinato nome file, puoi estendere temporaneamente il percorso e ripristinare il percorso di sistema nel blocco finale riferimento:

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

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 da module.

Ho creato un pacchetto che utilizza imp per te.Lo chiamo import_file e questo è come viene utilizzato:

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

Puoi ottenerlo presso:

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

o a

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

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)

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

cioè:

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

Python creerà /absolute/path/to/script/module.pyc e lo aggiornerà se cambi il contenuto di /absolute/path/to/module/module.py

quindi includere quanto segue in mypythonscript.py

from module import *

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


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

Ma se ce la fai senza guardia puoi finalmente fare un percorso molto lungo

Una soluzione semplice utilizzando importlib invece del imp pacchetto (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 è questo non abbiamo nemmeno bisogno di conoscere il nome effettivo del modulo che vorremmo importare, per poterlo utilizzare nel nostro codice.Questo è utile, ad es.nel caso in cui il percorso del modulo sia un argomento configurabile.

Aggiungendolo all'elenco delle risposte poiché non sono riuscito a trovare nulla che funzionasse.Ciò consentirà l'importazione di moduli Python compilati (pyd) nella versione 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()

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

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 sicuramente possibilità di miglioramento e facoltativi __init__.py i file potrebbero richiedere altre modifiche, ma se lo hai __init__.py in generale, questo fa il trucco.

Il modo migliore, penso, è dalla documentazione ufficiale (29.1.imp: accedi agli interni di importazione):

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()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top