Eu quero herdar de uma classe em um arquivo que se encontra em um diretório acima do atual.
É possível importar relativamente esse arquivo?
Eu quero herdar de uma classe em um arquivo que se encontra em um diretório acima do atual.
É possível importar relativamente esse arquivo?
Respostas:
from ..subpkg2 import mod
De acordo com os documentos do Python: Quando estiver dentro de uma hierarquia de pacotes, use dois pontos, como diz o documento da instrução de importação :
Ao especificar qual módulo importar, você não precisa especificar o nome absoluto do módulo. Quando um módulo ou pacote está contido em outro pacote, é possível fazer uma importação relativa no mesmo pacote superior sem precisar mencionar o nome do pacote. Usando pontos iniciais no módulo ou pacote
from
especificado, você pode especificar a altura de percorrer a hierarquia atual do pacote sem especificar nomes exatos. Um ponto inicial significa o pacote atual em que o módulo que está fazendo a importação existe. Dois pontos significam um nível de pacote . Três pontos sobem dois níveis, etc. Portanto, se você executar afrom . import mod
partir de um módulo nopkg
pacote, você terminará importandopkg.mod
. Se você executarfrom ..subpkg2 import mod
de dentro,pkg.subpkg1
você importarápkg.subpkg2.mod
. A especificação para importações relativas está contida no PEP 328 .
O PEP 328 trata de importações absolutas / relativas.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
A resposta do @ gimel está correta se você pode garantir a hierarquia de pacotes que ele menciona. Se você não pode - se sua necessidade real é a que você expressou, exclusivamente vinculada a diretórios e sem qualquer relação necessária com o empacotamento -, você precisa trabalhar __file__
para descobrir o diretório pai (algumas os.path.dirname
chamadas serão necessárias; -), então (se esse diretório não estiver ligado sys.path
) preceder temporariamente inserção disse dir no início de sys.path
, __import__
, retire disse dir novamente - o trabalho sujo de fato, mas, "quando você deve, você deve" (e se esforça para Pyhon nunca impeça o programador de fazer o que deve ser feito - assim como o padrão ISO C diz na seção "Spirit of C" em seu prefácio! -).
Aqui está um exemplo que pode funcionar para você:
import sys
import os.path
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import module_in_parent_dir
sys.path
disponibilizar o mesmo módulo com nomes diferentes e todos os erros correspondentes. autopath.py em pypy ou _preamble.py em twisted resolva-o usando um critério de pesquisa que identifica o pacote de nível superior enquanto percorre os diretórios para cima.
sys.path.remove(pathYouJustAdded)
após a importação necessária para não manter esse novo caminho.
Importe o módulo de um diretório que esteja exatamente um nível acima do diretório atual:
from .. import module
from .. import module
I got error ValueError: Tentativa de importação relativa no pacote não seguindo a recomendação
prefácio: reescrevi substancialmente uma resposta anterior com a esperança de ajudar as pessoas a entrar no ecossistema do python, e espero dar a todos a melhor mudança de sucesso com o sistema de importação do python.
Isso cobrirá as importações relativas dentro de um pacote , que eu acho que é o caso mais provável da pergunta do OP.
É por isso que escrevemos import foo
para carregar um módulo "foo" no namespace raiz, em vez de escrever:
foo = dict(); # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh: # please avoid doing this
exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo) # please avoid doing this
É por isso que podemos incorporar python em ambientes onde não há um sistema de arquivos defacto sem fornecer um sistema virtual, como o Jython.
A dissociação de um sistema de arquivos permite que as importações sejam flexíveis; esse design permite coisas como importações de arquivos compactados / zip, singletons de importação, cache de bytecode, extensões cffi e até carregamento remoto de definição de código.
Portanto, se as importações não estão acopladas a um sistema de arquivos, o que significa "um diretório ativo"? Temos que escolher algumas heurísticas, mas podemos fazer isso, por exemplo, ao trabalhar em um pacote , já foram definidas algumas heurísticas que fazem com que as importações relativas gostem .foo
e..foo
funcionem dentro do mesmo pacote. Legal!
Se você deseja sinceramente associar seus padrões de carregamento de código-fonte a um sistema de arquivos, você pode fazer isso. Você terá que escolher suas próprias heurísticas, e usar algum tipo de importação de máquinas, eu recomendo importlib
O exemplo importlib do Python se parece com isso:
import importlib.util
import sys
# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'
foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)
foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton
Há um ótimo exemplo de projeto disponível oficialmente aqui: https://github.com/pypa/sampleproject
Um pacote python é uma coleção de informações sobre seu código-fonte, que pode informar outras ferramentas sobre como copiar seu código-fonte em outros computadores e como integrar seu código-fonte no caminho do sistema para que import foo
funcione em outros computadores (independentemente do intérprete, sistema operacional do host etc.)
Vamos ter um nome de pacote foo
, em algum diretório (de preferência um diretório vazio).
some_directory/
foo.py # `if __name__ == "__main__":` lives here
Minha preferência é criar setup.py
como irmão foo.py
, porque isso simplifica a gravação do arquivo setup.py, no entanto, você pode escrever a configuração para alterar / redirecionar tudo o que o setuptools faz por padrão, se desejar; por exemplo, colocar foo.py
em um diretório "src /" é um pouco popular, não abordado aqui.
some_directory/
foo.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
py_modules=['foo'],
)
.
python3 -m pip install --editable ./ # or path/to/some_directory/
"editável" -e
, mais uma vez, redirecionará o mecanismo de importação para carregar os arquivos de origem nesse diretório, em vez de copiar os arquivos exatos atuais para a biblioteca do ambiente de instalação. Isso também pode causar diferenças de comportamento na máquina de um desenvolvedor, não deixe de testar seu código! Existem outras ferramentas além do pip, no entanto, eu recomendo que o pip seja o introdutório :)
Também gosto de criar foo
um "pacote" (um diretório que contém __init__.py
) em vez de um módulo (um único arquivo ".py"); ambos "pacotes" e "módulos" podem ser carregados no espaço para nome raiz, os módulos permitem espaços para nome aninhados, o que é útil se quisermos importar "um diretório relativo acima".
some_directory/
foo/
__init__.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
)
Eu também gosto de fazer a foo/__main__.py
, isso permite que o python execute o pacote como um módulo, por exemplo python3 -m foo
, execute foo/__main__.py
como __main__
.
some_directory/
foo/
__init__.py
__main__.py # `if __name__ == "__main__":` lives here, `def main():` too!
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
...
entry_points={
'console_scripts': [
# "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
'foo=foo.__main__:main',
]
},
)
Permite detalhar isso com mais alguns módulos: Basicamente, você pode ter uma estrutura de diretórios assim:
some_directory/
bar.py # `import bar`
foo/
__init__.py # `import foo`
__main__.py
baz.py # `import foo.baz
spam/
__init__.py # `import foo.spam`
eggs.py # `import foo.spam.eggs`
setup.py
setup.py
convencionalmente contém informações de metadados sobre o código-fonte, como:
foo
, embora seja popular substituir hífens por sublinhadospython ./setup.py test
É muito expansivo, podendo até compilar extensões em tempo real se um módulo de origem estiver sendo instalado em uma máquina de desenvolvimento. Para um exemplo diário, recomendo o setup.py do repositório de amostras PYPA
Se você estiver liberando um artefato de construção, por exemplo, uma cópia do código destinado a executar computadores quase idênticos, um arquivo requirements.txt é uma maneira popular de capturar informações exatas de dependência, em que "install_requires" é uma boa maneira de capturar o mínimo e versões máximas compatíveis. No entanto, como as máquinas de destino são praticamente idênticas, eu recomendo criar um tarball de um prefixo python inteiro. Isso pode ser complicado, muito detalhado para entrar aqui. Confira pip install
's --target
opção, ou virtualenv aka venv para leads.
de volta ao exemplo
Em foo / spam / eggs.py, se quiséssemos código de foo / baz, poderíamos solicitá-lo por seu espaço de nome absoluto:
import foo.baz
Se desejássemos reservar a capacidade de mover eggs.py para outro diretório no futuro com alguma outra baz
implementação relativa , poderíamos usar uma importação relativa como:
import ..baz
Para carregar o código python de forma confiável, tenha esse código em um módulo e esse módulo instalado na biblioteca do python.
Os módulos instalados sempre podem ser carregados no namespace de nível superior com import <name>
Há um ótimo projeto de amostra disponível oficialmente aqui: https://github.com/pypa/sampleproject
Basicamente, você pode ter uma estrutura de diretórios assim:
the_foo_project/
setup.py
bar.py # `import bar`
foo/
__init__.py # `import foo`
baz.py # `import foo.baz`
faz/ # `import foo.faz`
__init__.py
daz.py # `import foo.faz.daz` ... etc.
.
Certifique-se de declarar o seu setuptools.setup()
em setup.py
,
exemplo oficial: https://github.com/pypa/sampleproject/blob/master/setup.py
No nosso caso, provavelmente queremos exportar bar.py
e foo/__init__.py
, meu breve exemplo:
#!/usr/bin/env python3
import setuptools
setuptools.setup(
...
py_modules=['bar'],
packages=['foo'],
...
entry_points={},
# Note, any changes to your setup.py, like adding to `packages`, or
# changing `entry_points` will require the module to be reinstalled;
# `python3 -m pip install --upgrade --editable ./the_foo_project
)
.
Agora podemos instalar nosso módulo na biblioteca python; com o pip, você pode instalar the_foo_project
na sua biblioteca python no modo de edição, para que possamos trabalhar em tempo real
python3 -m pip install --editable=./the_foo_project
# if you get a permission error, you can always use
# `pip ... --user` to install in your user python library
.
Agora, a partir de qualquer contexto python, podemos carregar nossos py_modules e pacotes compartilhados
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
pip install --edit foo
, quase sempre dentro de um virtualenv. Quase nunca escrevo um módulo que não se destina a ser instalado. se eu entendi mal algo que gostaria de saber.
editable
links de ovo e módulos python instalados não são exatamente iguais em todos os aspectos; por exemplo, adicionar um novo espaço para nome a um módulo editável será encontrado na pesquisa de caminho, mas se isso não for exportado no arquivo setup.py, ele não será empacotado / instalado! Teste seu caso de uso :)