Como importar um módulo dado o caminho completo?
-
09-06-2019 - |
Pergunta
Como posso carregar um módulo Python dado seu caminho completo?Observe que o arquivo pode estar em qualquer lugar do sistema de arquivos, pois é uma opção de configuração.
Solução
Para uso em Python 3.5+:
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 e 3.4, use:
from importlib.machinery import SourceFileLoader
foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()
(Embora isso tenha sido descontinuado no Python 3.4.)
Para uso em Python 2:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Existem funções de conveniência equivalentes para arquivos Python e DLLs compilados.
Veja também http://bugs.python.org/issue21436.
Outras dicas
A vantagem de adicionar um caminho ao sys.path (em vez de imp) é que isso simplifica as coisas ao importar mais de um módulo de um único pacote.Por exemplo:
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
Você também pode fazer algo assim e adicionar o diretório onde o arquivo de configuração está localizado ao caminho de carregamento do Python e, em seguida, fazer uma importação normal, supondo que você saiba o nome do arquivo com antecedência, neste caso "config".
Confuso, mas funciona.
configfile = '~/config.py'
import os
import sys
sys.path.append(os.path.dirname(os.path.expanduser(configfile)))
import config
Parece que você não deseja importar especificamente o arquivo de configuração (que tem muitos efeitos colaterais e complicações adicionais envolvidas), você apenas deseja executá-lo e poder acessar o namespace resultante.A biblioteca padrão fornece uma API especificamente para isso na forma de runpy.run_path:
from runpy import run_path
settings = run_path("/path/to/file.py")
Essa interface está disponível em Python 2.7 e Python 3.2+
Se o seu módulo de nível superior não for um arquivo, mas estiver empacotado como um diretório com __init__.py, então a solução aceita quase funciona, mas não exatamente.No Python 3.5+, o seguinte código é necessário (observe a linha adicionada que começa com '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)
Sem esta linha, quando exec_module é executado, ele tenta vincular as importações relativas em seu __init__.py de nível superior ao nome do módulo de nível superior - neste caso, "mymodule".Mas "mymodule" ainda não foi carregado, então você receberá o erro "SystemError:O módulo pai 'mymodule' não foi carregado, não é possível realizar a importação relativa".Portanto, você precisa vincular o nome antes de carregá-lo.A razão para isto é o invariante fundamental do sistema de importação relativa:"A retenção invariável é que se você tiver sys.modules['spam'] e sys.modules['spam.foo'] (como faria após a importação acima), o último deve aparecer como o atributo foo do primeiro" como discutido aqui.
Eu criei uma versão ligeiramente modificada do Resposta maravilhosa de @SebastianRittau (para Python> 3.4, eu acho), que permitirá carregar um arquivo com qualquer extensão como um módulo usando spec_from_loader
em vez 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)
A vantagem de codificar o caminho de forma explícita SourceFileLoader
é esse o maquinaria não tentará descobrir o tipo de arquivo a partir da extensão.Isso significa que você pode carregar algo como um .txt
arquivo usando este método, mas você não poderia fazer isso com spec_from_file_location
sem especificar o carregador porque .txt
não está em importlib.machinery.SOURCE_SUFFIXES
.
Aqui está um código que funciona em todas as versões do Python, de 2.7 a 3.5 e provavelmente até em outras.
config_file = "/tmp/config.py"
with open(config_file) as f:
code = compile(f.read(), config_file, 'exec')
exec(code, globals(), locals())
Eu testei.Pode ser feio mas até agora é o único que funciona em todas as versões.
Para importar seu módulo, você precisa adicionar seu diretório à variável de ambiente, temporária ou permanentemente.
Temporariamente
import sys
sys.path.append("/path/to/my/modules/")
import my_module
Permanentemente
Adicionando a seguinte linha ao seu .bashrc
arquivo (no linux) e execute source ~/.bashrc
no terminal:
export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"
Crédito/Fonte: saarrr, outra pergunta sobre stackexchange
Você quer dizer carregar ou importar?
Você pode manipular a lista sys.path, especificar o caminho para o seu módulo e, em seguida, importar o seu módulo.Por exemplo, dado um módulo em:
/foo/bar.py
Você poderia fazer:
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')
Eu acredito que você pode usar imp.find_module()
e imp.load_module()
para carregar o módulo especificado.Você precisará separar o nome do módulo do caminho, ou seja,se você quisesse carregar /home/mypath/mymodule.py
você precisaria fazer:
imp.find_module('mymodule', '/home/mypath/')
...mas isso deve dar conta do recado.
Isso deve 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)
Você pode usar o pkgutil
módulo (especificamente o walk_packages
método) para obter uma lista dos pacotes no diretório atual.A partir daí é trivial usar o importlib
máquinas para importar os módulos que você deseja:
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!
Esta área do Python 3.4 parece ser extremamente tortuosa de entender!No entanto, com um pouco de hacking usando o código de Chris Calloway para começar, consegui fazer algo funcionar.Aqui está a função 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
Parece usar módulos não obsoletos do Python 3.4.Não pretendo entender por quê, mas parece funcionar dentro de um programa.Descobri que a solução de Chris funcionou na linha de comando, mas não dentro de um programa.
Não estou dizendo que seja melhor, mas para ser mais completo, gostaria de sugerir o exec
função, disponível em python 2 e 3.exec
permite executar código arbitrário no escopo global ou em um escopo interno, fornecido como um dicionário.
Por exemplo, se você tiver um módulo armazenado em "/path/to/module
"com a função foo()
, você poderia executá-lo fazendo o seguinte:
module = dict()
with open("/path/to/module") as f:
exec(f.read(), module)
module['foo']()
Isso torna um pouco mais explícito que você está carregando código dinamicamente e concede algum poder adicional, como a capacidade de fornecer recursos internos personalizados.
E se ter acesso através de atributos, em vez de chaves, for importante para você, você pode criar uma classe dict personalizada para os globais, que forneça esse acesso, por exemplo:
class MyModuleClass(dict):
def __getattr__(self, name):
return self.__getitem__(name)
Para importar um módulo de um determinado nome de arquivo, você pode estender temporariamente o caminho e restaurar o caminho do sistema no bloco final referência:
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
Crie o módulo python test.py
import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3
Crie o módulo python test_check.py
from test import Client1
from test import Client2
from test import test3
Podemos importar o módulo importado do módulo.
Eu fiz um pacote que usa imp
para você.eu chamo isso import_file
e é assim que é usado:
>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')
Você pode obtê-lo em:
http://pypi.python.org/pypi/import_file
ou em
Importe módulos de pacote em tempo de execução (receita 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)
No Linux, adicionar um link simbólico no diretório em que seu script python está localizado funciona.
ou seja:
ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py
python irá criar /absolute/path/to/script/module.pyc
e irá atualizá-lo se você alterar o conteúdo de /absolute/path/to/module/module.py
então inclua o seguinte em mypythonscript.py
from module import *
maneira bastante simples:suponha que você queira importar o arquivo com caminho relativo ../../MyLibs/pyfunc.py
libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf
Mas se você conseguir sem guarda, poderá finalmente percorrer um caminho muito longo
Uma solução simples usando importlib
ao invés de imp
pacote (testado para Python 2.7, embora deva funcionar também 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")
Agora você pode usar diretamente o namespace do módulo importado, assim:
a = module.myvar
b = module.myfunc(a)
A vantagem desta solução é que nem precisamos saber o nome real do módulo que gostaríamos de importar, para usá-lo em nosso código.Isto é útil, por ex.caso o caminho do módulo seja um argumento configurável.
Adicionando isso à lista de respostas, pois não consegui encontrar nada que funcionasse.Isso permitirá importações de módulos python compilados (pyd) em 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()
Esta resposta é um complemento à resposta de Sebastian Rittau em resposta ao comentário:"Mas e se você não tiver o nome do módulo?" Esta é uma maneira rápida e suja de obter o provável nome do módulo Python, dado um nome de arquivo - apenas sobe a árvore até encontrar um diretório sem um __init__.py
arquivo e depois o transforma novamente em um nome de arquivo.Para Python 3.4+ (usa pathlib), o que faz sentido, já que as pessoas do Py2 podem usar "imp" ou outras formas de fazer importações 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))
Existem certamente possibilidades de melhoria, e a opção opcional __init__.py
arquivos podem exigir outras alterações, mas se você tiver __init__.py
em geral, isso resolve.
A melhor maneira, eu acho, é a partir da documentação oficial (29.1.imp — Acesse os internos de importação):
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()