¿Cómo importar un módulo dada la ruta completa?

1142

¿Cómo puedo cargar un módulo de Python dada su ruta completa? Tenga en cuenta que el archivo puede estar en cualquier parte del sistema de archivos, ya que es una opción de configuración.

derfred
fuente
21
Pregunta agradable y simple, y respuestas útiles, pero me hacen preguntarme qué sucedió con el mantra de Python "Hay una manera obvia " de hacerlo ... No parece nada como una sola respuesta simple o obvia. Parece ridículamente hacky y dependiente de la versión para una operación tan fundamental (y parece más hinchada en las versiones más nuevas ...).
Inger

Respuestas:

1283

Para Python 3.5+ use:

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

Para Python 3.3 y 3.4 use:

from importlib.machinery import SourceFileLoader

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

(Aunque esto ha quedado en desuso en Python 3.4.)

Para Python 2 use:

import imp

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

Hay funciones de conveniencia equivalentes para archivos compilados de Python y DLL.

Ver también http://bugs.python.org/issue21436 .

Sebastian Rittau
fuente
53
Si supiera el espacio de nombres - 'module.name' - ya lo usaría __import__.
Sridhar Ratnakumar
62
@SridharRatnakumar el valor del primer argumento de imp.load_sourcesolo establece el .__name__del módulo devuelto. No afecta la carga.
Dan D.
17
@DanD. - el primer argumento de imp.load_source()determina la clave de la nueva entrada creada en el sys.modulesdiccionario, por lo que el primer argumento sí afecta la carga.
Brandon Rhodes
99
El impmódulo está en desuso desde la versión 3.4: el imppaquete está pendiente de desuso a favor de importlib.
Chiel ten Brinke
99
@AXO y más hasta el punto uno se pregunta por qué algo tan simple y básico como esto tiene que ser tan complicado. No está en muchos otros idiomas.
rocoso
424

La ventaja de agregar una ruta a sys.path (sobre el uso de imp) es que simplifica las cosas al importar más de un módulo desde un solo paquete. Por ejemplo:

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
Daryl Spitzer
fuente
13
¿Cómo usamos sys.path.appendpara apuntar a un único archivo de Python en lugar de un directorio?
Phani
28
:-) Quizás su pregunta sería más adecuada como una pregunta de StackOverflow, no como un comentario en una respuesta.
Daryl Spitzer
3
La ruta de Python puede contener archivos zip, "huevos" (un tipo complejo de archivos zip), etc. Se pueden importar módulos de ellos. Por lo tanto, los elementos de la ruta son contenedores de archivos, pero no son necesariamente directorios.
alexis
12
Tenga cuidado con el hecho de que Python almacena en caché las declaraciones de importación. En el raro caso de que tenga dos carpetas diferentes que compartan un solo nombre de clase (classX), el enfoque de agregar una ruta a sys.path, importar classX, eliminar la ruta y repetir las rutas restantes no funcionará. Python siempre cargará la clase desde la primera ruta desde su caché. En mi caso, mi objetivo era crear un sistema de complementos donde todos los complementos implementen una clase X específica. Terminé usando SourceFileLoader , tenga en cuenta que su desaprobación es controvertido .
ComFreek
3
Tenga en cuenta que este enfoque permite que el módulo importado importe otros módulos desde el mismo directorio, que los módulos suelen hacer, mientras que el enfoque de la respuesta aceptada no lo hace (al menos en 3.7). importlib.import_module(mod_name)se puede usar en lugar de la importación explícita aquí si no se conoce el nombre del módulo en tiempo de ejecución. Sin embargo, agregaría un sys.path.pop()al final, suponiendo que el código importado no intente importar más módulos a medida que se usa.
Eli_B
43

Si su módulo de nivel superior no es un archivo pero está empaquetado como un directorio con __init__.py, entonces la solución aceptada casi funciona, pero no del todo. En Python 3.5+ se necesita el siguiente código (tenga en cuenta la línea agregada que comienza 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)

Sin esta línea, cuando se ejecuta exec_module, intenta vincular las importaciones relativas en su nivel superior __init__.py con el nombre del módulo de nivel superior, en este caso "mymodule". Pero "mymodule" aún no está cargado, por lo que obtendrá el error "SystemError: el módulo principal 'mymodule' no está cargado, no puede realizar una importación relativa". Por lo tanto, debe vincular el nombre antes de cargarlo. La razón de esto es la invariante fundamental del sistema de importación relativo: "La retención invariable es que si tiene sys.modules ['spam'] y sys.modules ['spam.foo'] (como lo haría después de la importación anterior ), este último debe aparecer como el atributo foo del primero " como se explica aquí .

Sam Grondahl
fuente
¡Muchas gracias! Este método permite importaciones relativas entre submódulos. ¡Excelente!
tebanep
Esta respuesta coincide con la documentación aquí: docs.python.org/3/library/… .
Tim Ludwinski
1
pero lo que es mymodule?
Gulzar
@Gulzar, es el nombre que quieras darle a tu módulo, de modo que luego puedas hacer: "desde mymodule import myclass"
Idodo
Entonces ... en /path/to/your/module/realidad /path/to/your/PACKAGE/? y a que mymodulete refieres myfile.py?
Gulzar
37

Para importar su módulo, debe agregar su directorio a la variable de entorno, ya sea temporal o permanentemente.

Temporalmente

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

Permanentemente

Agregar la siguiente línea a su .bashrcarchivo (en Linux) y ejecutar source ~/.bashrcen el terminal:

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

Crédito / Fuente: saarrrr , otra pregunta de stackexchange

Miladiosos
fuente
3
Esta solución "temporal" es una gran respuesta si desea pinchar un proyecto en un cuaderno jupyter en otro lugar.
fordy
Pero ... es peligroso alterar el camino
Shai Alon
@ShaiAlon Está agregando rutas, por lo que no hay peligro más que cuando transfiere códigos de una computadora a otra, las rutas pueden desordenarse. Entonces, para el desarrollo de paquetes, solo importo paquetes locales. Además, los nombres de los paquetes deben ser únicos. Si está preocupado, use la solución temporal.
Miladiouss
28

Parece que no desea importar específicamente el archivo de configuración (que tiene muchos efectos secundarios y complicaciones adicionales involucradas), solo desea ejecutarlo y poder acceder al espacio de nombres resultante. La biblioteca estándar proporciona una API específicamente para eso en forma de runpy.run_path :

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

Esa interfaz está disponible en Python 2.7 y Python 3.2+

ncoghlan
fuente
Me gusta este método, pero cuando obtengo el resultado de run_path es un diccionario al que parece que no puedo acceder.
Stephen Ellwood
¿Qué quiere decir con "no se puede acceder"? No se puede importar de él (por eso esto es sólo una opción buena cuando en realidad no es necesario el acceso al estilo de importación), pero el contenido debe estar disponible a través de la API dict regular ( result[name], result.get('name', default_value), etc.)
ncoghlan
Esta respuesta está muy subestimada. ¡Es muy corto y simple! Aún mejor, si necesita un espacio de nombres de módulo adecuado, puede hacer algo como from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo
20

También puede hacer algo como esto y agregar el directorio en el que se encuentra el archivo de configuración en la ruta de carga de Python, y luego simplemente hacer una importación normal, suponiendo que conoce el nombre del archivo de antemano, en este caso "config".

Desordenado, pero funciona.

configfile = '~/config.py'

import os
import sys

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

import config
ctcherry
fuente
Eso no es dinámicamente.
Shai Alon
Intenté: 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: ningún módulo llamado config_file"
Shai Alon
17

Puedes usar el

load_source(module_name, path_to_file) 

Método del módulo imp .

zuber
fuente
... y imp.load_dynamic(module_name, path_to_file)para archivos DLL
HEKTO
34
recuerda que el diablillo está en desuso ahora.
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')
Chris Calloway
fuente
37
¿Por qué escribir 14 líneas de código con errores cuando esto ya es abordado por la biblioteca estándar? No ha realizado la comprobación de errores en el formato o el contenido de full_path_to_module o las operaciones os.whatever; y usar una except:cláusula general rara vez es una buena idea.
Chris Johnson
Deberías usar más "try-finally" aquí. Por ejemplosave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
kay, SE es malo
11
@ChrisJohnson this is already addressed by the standard library, sí, pero Python tiene la desagradable costumbre de no ser compatible con versiones anteriores ... ya que la respuesta marcada dice que hay 2 formas diferentes antes y después de 3.3. En ese caso, preferiría escribir mi propia función universal que verificar la versión sobre la marcha. Y sí, tal vez este código no está demasiado bien protegido contra errores, pero muestra una idea (que es os.chdir (), no lo he pensado), basándome en que puedo escribir un código mejor. Por lo tanto, +1.
Sushi271
13

Aquí hay un código que funciona en todas las versiones de Python, desde 2.7-3.5 y probablemente incluso en otras.

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

Lo probé Puede ser feo, pero hasta ahora es el único que funciona en todas las versiones.

Sorin
fuente
1
Esta respuesta funcionó para mí donde load_sourceno lo hizo porque importa el script y proporciona el acceso del script a los módulos y globales en el momento de la importación.
Klik
13

Se me ocurrió una versión ligeramente modificada de la maravillosa respuesta de @ SebastianRittau (para Python> 3.4, creo), que le permitirá cargar un archivo con cualquier extensión como módulo usando en spec_from_loaderlugar de 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)

La ventaja de codificar la ruta de forma explícita SourceFileLoaderes que la maquinaria no intentará averiguar el tipo de archivo desde la extensión. Esto significa que puede cargar algo como un .txtarchivo usando este método, pero no podría hacerlo spec_from_file_locationsin especificar el cargador porque .txtno está en importlib.machinery.SOURCE_SUFFIXES.

Físico loco
fuente
13

¿Te refieres a cargar o importar?

Puede manipular la sys.pathlista, especificar la ruta a su módulo y luego importar su módulo. Por ejemplo, dado un módulo en:

/foo/bar.py

Podrías hacerlo:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
Trigo
fuente
1
@Wheat ¿Por qué sys.path [0: 0] en lugar de sys.path [0]?
user618677
55
B / c sys.path [0] = xy sobrescribe el primer elemento de ruta mientras que la ruta [0: 0] = xy es equivalente a path.insert (0, xy)
dom0
2
hm el path.insert funcionó para mí pero el truco [0: 0] no lo hizo.
jsh
11
sys.path[0:0] = ['/foo']
Kevin Edwards
66
Explicit is better than implicit.Entonces, ¿por qué no en sys.path.insert(0, ...)lugar de sys.path[0:0]?
winklerrr
8

Creo que puede usar imp.find_module()y imp.load_module()cargar el módulo especificado. Tendrá que separar el nombre del módulo de la ruta, es decir, si desea cargar /home/mypath/mymodule.pydebería hacer:

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

... pero eso debería hacer el trabajo.

Mate
fuente
6

Puede usar el pkgutilmódulo (específicamente el walk_packagesmétodo) para obtener una lista de los paquetes en el directorio actual. A partir de ahí, es trivial usar la importlibmaquinaria para importar los módulos que desee:

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!
bob_twinkles
fuente
5

Crear el módulo python test.py

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

Crear módulo de python test_check.py

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

Podemos importar el módulo importado desde el módulo.

abhimanyu
fuente
4

¡Esta área de Python 3.4 parece ser extremadamente tortuosa de entender! Sin embargo, con un poco de pirateo usando el código de Chris Calloway como un comienzo, logré que algo funcionara. Aquí está la función básica.

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

Esto parece utilizar módulos no obsoletos de Python 3.4. No pretendo entender por qué, pero parece funcionar desde un programa. Encontré que la solución de Chris funcionaba en la línea de comando pero no desde dentro de un programa.

Redlegjed
fuente
4

No digo que sea mejor, pero en aras de la exhaustividad, quería sugerir que la execfunción, disponible en python 2 y 3. le execpermite ejecutar código arbitrario en el ámbito global o en un ámbito interno, proporcionado como un diccionario.

Por ejemplo, si tiene un módulo almacenado en "/path/to/module"con la función foo(), puede ejecutarlo haciendo lo siguiente:

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

Esto hace que sea un poco más explícito que esté cargando código dinámicamente y le otorga algo de potencia adicional, como la capacidad de proporcionar incorporaciones personalizadas.

Y si tener acceso a través de los atributos, en lugar de claves, es importante para usted, puede diseñar una clase dict personalizada para los globales, que proporciona dicho acceso, por ejemplo:

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

Para importar un módulo de un nombre de archivo dado, puede extender temporalmente la ruta y restaurar la ruta del sistema en la referencia de bloqueo final:

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
Peter Zhu
fuente
4

Si tenemos scripts en el mismo proyecto pero en un directorio diferente, podemos resolver este problema con el siguiente método.

En esta situación utils.pyestá ensrc/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
Kumar Immanuel
fuente
la más simple de la OMI
Ardhi
3

Esto debería funcionar

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)
Hengjie
fuente
44
Una forma más general para cortar el cabo de extensión es: name, ext = os.path.splitext(os.path.basename(infile)). Su método funciona porque la restricción anterior a la extensión .py. Además, probablemente debería importar el módulo a alguna entrada de variable / diccionario.
ReneSac
2

Hice un paquete que usa imppara ti. Lo llamo import_filey así es como se usa:

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

Puedes conseguirlo en:

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

o en

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

ubershmekel
fuente
1
os.chdir? (caracteres mínimos para aprobar el comentario).
ychaouche
He pasado todo el día resolviendo un error de importación en un exe generado por pyinstaller. Al final, esto es lo único que funcionó para mí. ¡Muchas gracias por hacer esto!
frakman1
2

Importar módulos de paquetes en tiempo de ejecución (receta de 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)
usuario10370
fuente
2

En Linux, agregar un enlace simbólico en el directorio donde se encuentra su script de Python funciona.

es decir:

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

Python creará /absolute/path/to/script/module.pycy lo actualizará si cambia el contenido de/absolute/path/to/module/module.py

luego incluya lo siguiente en mypythonscript.py

from module import *
usuario2760152
fuente
1
Este es el truco que utilicé y me ha causado algunos problemas. Uno de los más dolorosos fue que IDEA tiene un problema en el que no recoge el código alterado desde el enlace, pero intenta guardar lo que cree que está allí. Una condición de carrera donde lo último que se ahorra es lo que se pega ... Perdí una cantidad decente de trabajo debido a esto.
Gripp
@Gripp no ​​está seguro si entiendo su problema, pero edito con frecuencia (casi exclusivamente) mis scripts en un servidor remoto desde mi escritorio a través de SFTP con un cliente como CyberDuck, y en ese caso también es una mala idea intentar y edite el archivo enlazado, en cambio es mucho más seguro editar el archivo original. Puede detectar algunos de estos problemas utilizando gity verificando git statuspara verificar que sus cambios en el script realmente están volviendo al documento fuente y no se pierden en el éter.
user5359531
2

He escrito mi propia función de importación global y portátil, basada en el importlibmódulo, para:

  • Poder importar tanto el módulo como un submódulo e importar el contenido de un módulo a un módulo principal (o a un global si no tiene un módulo principal).
  • Poder importar módulos con caracteres de punto en un nombre de archivo.
  • Poder importar módulos con cualquier extensión.
  • Poder usar un nombre independiente para un submódulo en lugar de un nombre de archivo sin extensión, que es el predeterminado.
  • Sea capaz de definir el orden de importación en función del módulo importado previamente en lugar de depender sys.pathde un almacenamiento de ruta de búsqueda.

La estructura del directorio de ejemplos:

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

Inclusión dependencia y orden:

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

Implementación:

Última tienda de cambios: 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')

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

Probado en Python 3.7.4, 3.2.5,2.7.16

Pros :

  • Puede importar ambos módulos como un submódulo y puede importar el contenido de un módulo a un módulo principal (o a un global si no tiene un módulo principal).
  • Puede importar módulos con puntos en un nombre de archivo.
  • Puede importar cualquier módulo de extensión desde cualquier módulo de extensión.
  • Puede usar un nombre independiente para un submódulo en lugar de un nombre de archivo sin extensión, que es por defecto (por ejemplo, testlib.std.pyas testlib, testlib.blabla.pyas , testlib_blablaetc.).
  • No depende de un sys.pathalmacenamiento de rutas de búsqueda.
  • No requiere guardar / restaurar variables globales como SOURCE_FILEy SOURCE_DIRentre llamadas a tkl_import_module.
  • [para 3.4.xy superior] Puede mezclar los espacios de nombres del módulo en tkl_import_modulellamadas anidadas (ej .: named->local->namedo local->named->localetc.).
  • [para 3.4.xy superior] Puede exportar automáticamente variables / funciones / clases globales desde donde se declara a todos los módulos secundarios importados a través de tkl_import_module(a través de la tkl_declare_globalfunción)

Contras :

  • [para 3.3.xe inferior] Requiere declarar tkl_import_moduleen todos los módulos a qué llamadas tkl_import_module(duplicación de código)

Actualización 1,2 (solo para versiones3.4.x superiores):

En Python 3.4 y superior, puede omitir el requisito de declarar tkl_import_moduleen cada módulo declarando tkl_import_moduleen un módulo de nivel superior y la función se inyectaría a todos los módulos secundarios en una sola llamada (es una especie de importación de autodespliegue).

Actualización 3 :

Se agregó la función tkl_source_modulecomo análogo a bash sourcecon protección de ejecución de soporte en la importación (implementado a través de la fusión del módulo en lugar de la importación).

Actualización 4 :

Se agregó la función tkl_declare_globalpara exportar automáticamente una variable global de módulo a todos los módulos secundarios donde una variable global de módulo no es visible porque no es parte de un módulo secundario.

Actualización 5 :

Todas las funciones se han movido a la biblioteca tacklelib, vea el enlace de arriba

Andry
fuente
2

Hay un paquete dedicado específicamente a esto:

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

Se ha probado en todas las versiones de Python (Jython y PyPy también), pero puede ser excesivo dependiendo del tamaño de su proyecto.

fny
fuente
1

Agregando esto a la lista de respuestas ya que no pude encontrar nada que funcionó. Esto permitirá importar módulos compilados (pyd) de Python en 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()
David
fuente
1

manera bastante simple: suponga que desea importar un archivo con una ruta relativa ../../MyLibs/pyfunc.py


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

Pero si lo logras sin guardia, finalmente puedes obtener un camino muy largo

Andrei Keino
fuente
1

Una solución simple que usa en importliblugar del imppaquete (probado para Python 2.7, aunque también debería funcionar para 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")

Ahora puede usar directamente el espacio de nombres del módulo importado, así:

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

La ventaja de esta solución es que ni siquiera necesitamos saber el nombre real del módulo que nos gustaría importar para poder usarlo en nuestro código. Esto es útil, por ejemplo, en caso de que la ruta del módulo sea un argumento configurable.

Ataxias
fuente
De esta manera está modificando el sys.path, que no se ajusta a todos los casos de uso.
bgusach
@bgusach Esto puede ser cierto, pero también es deseable en algunos casos (agregar una ruta a sys.path simplifica las cosas al importar más de un módulo desde un solo paquete). En cualquier caso, si esto no es deseable, se puede hacer inmediatamente despuéssys.path.pop()
Ataxias
0

Esta respuesta es un complemento de la respuesta de Sebastian Rittau en respuesta al comentario: "pero ¿qué pasa si no tiene el nombre del módulo?" Esta es una forma rápida y sucia de obtener el nombre probable del módulo de Python con un nombre de archivo: solo sube el árbol hasta que encuentra un directorio sin un __init__.pyarchivo y luego lo vuelve a convertir en un nombre de archivo. Para Python 3.4+ (usa pathlib), lo cual tiene sentido ya que las personas Py2 pueden usar "imp" u otras formas de realizar importaciones relativas:

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

Ciertamente, hay posibilidades de mejora, y los __init__.pyarchivos opcionales pueden necesitar otros cambios, pero si lo tiene __init__.pyen general, este es el truco.

Michael Scott Cuthbert
fuente
-1

La mejor forma, creo, es a partir de la documentación oficial ( 29.1. Imp - Acceda a las partes internas de importación ):

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()
Zompa
fuente
1
Esta solución no le permite proporcionar la ruta, que es lo que pide la pregunta.
Micah Smith