1. Introdução
Em projetos reais, frequentemente precisamos importar dinamicamente módulos durante a execução do código e executar conteúdos específicos desses módulos. Normalmente, os nomes dos módulos e objetos a serem importados são passados como strings, por exemplo, "pacote.subpacote.funcao". Este artigo demonstra como realizar importações dinâmicas em Python.
Considere a seguinte estrutura de pacotes:
meu_pacote/
├── __init__.py
├── modulo_principal.py
└── subpacote/
├── __init__.py
└── modulo_util.py
O conteúdo de modulo_util.py é:
contador = 100
def executar_tarefa():
print("Tarefa executada com sucesso!")
Usaremos "meu_pacote.subpacote.modulo_util.executar_tarefa" como exemplo para importação dinâmica.
Método 1: Utilizando a função embutida import
A função __import__ permite importar módulos dinamicamente:
modulo = __import__(
"meu_pacote.subpacote.modulo_util", globals(), locals(), fromlist=["executar_tarefa"]
)
print(modulo)
print(getattr(modulo, "executar_tarefa"))
print(getattr(modulo, "contador"))
Resultado:
<module 'meu_pacote.subpacote.modulo_util' from '/caminho/para/meu_pacote/subpacote/modulo_util.py'>
<function executar_tarefa at 0x000002EC1CB9B250>
100
A assniatura da função é:
__import__(name, globals=None, locals=None, fromlist=(), level=0) -> modulo
Parâmetros:
- name: Nome do módulo a ser importado, incluindo o caminho completo.
- globals: Variáveis globais do escopo atual, normalmente definido como globals().
- locals: Variáveis locais do escopo atual, geralmente não necessário.
- fromlist: Controla quais componentes do módulo serão importados. Quando vazio, retorna o módulo raiz; quando contém elementos, retorna o submódulo especificado.
- level: Determina se o caminho é absoluto (0) ou relativo (número positivo indicando níveis de diretório pai).
Método 2: Usando importlib.import_module
O módulo importlib oferece uma interface mais amigável para importações dinâmicas:
from importlib import import_module
modulo = import_module(name="meu_pacote.subpacote.modulo_util")
print(modulo)
print(getattr(modulo, "executar_tarefa"))
print(getattr(modulo, "contador"))
A assinatura da função é:
import_module(name, package=None)
Parâmetros:
- name: Nome do pacote a ser importado.
- package: Nome do pacote para importações relativas. Quando definido, o parâmetro name deve referir-se a algo dentro deste pacote.
Exemplo com importação relativa:
modulo = import_module(name="..subpacote", package="meu_pacote.novo_pacote")
print(modulo)
print(getattr(modulo, "executar_tarefa"))
print(getattr(modulo, "contador"))
Este exemplo importa "meu_pacote.subpacote" a partir de "meu_pacote.novo_pacote".
Método 3: Utilizando exec (não recomendado)
É possível concatenar e executar código dinamicamente com exec, mas esta abordagem não é recomendada por razões de segurança e legibilidade:
caminho_modulo = "meu_pacote.subpacote.modulo_util"
exec(
"import {} as mod\n"
"print(mod.executar_tarefa())\n"
"print(mod.contador)\n".format(caminho_modulo)
)
Esta técnica é equivalente a importar o módulo localmente e executar o código como string, o que pode representar riscos de segurança.
Recarga de Módulos Dinâmicos
O módulo importlib também oferece a função reload, útil quando precisamos atualizar um módulo que foi modificado durante a execução:
from importlib import reload
# Primeira importação
modulo = import_module("meu_pacote.subpacote.modulo_util")
# Modificamos o arquivo modulo_util.py externamente
# Recarregando o módulo para aplicar as mudanças
modulo = reload(modulo)
Isso garante que as alterações no módulo sejam refletidas imediatamente sem a necessidade de reiniciar o aplicativo.