Como já usei pacotes simples, não esperava o problema que encontrei com pacotes aninhados. Aqui está…
Layout de diretório
dir
|
+-- test.py
|
+-- package
|
+-- __init__.py
|
+-- subpackage
|
+-- __init__.py
|
+-- module.py
Conteúdo de init .py
Ambos package/__init__.py
e package/subpackage/__init__.py
estão vazios.
Conteúdo de module.py
# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...
Conteúdo de test.py
(3 versões)
Versão 1
# file test.py
from package.subpackage.module import *
print attribute1 # OK
Essa é a maneira ruim e insegura de importar coisas (importar tudo em massa), mas funciona.
Versão 2
# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1
Uma maneira mais segura de importar, item por item, mas falha, o Python não quer isso: falha com a mensagem: "Nenhum módulo denominado módulo". Contudo …
# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here
… Diz <module 'package.subpackage.module' from '...'>
. Então isso é um módulo, mas não é um módulo / -P 8-O ... uh
Versão 3
# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK
Este funciona. Então você é forçado a usar o prefixo exagerado o tempo todo ou usar a forma insegura como na versão 1 e não permitida pelo Python para usar a forma prática e segura? A melhor maneira, que é segura e evita o prefixo longo desnecessário, é a única que o Python rejeita? É porque ama import *
ou porque ama prefixos longos demais (o que não ajuda a reforçar essa prática)?
Desculpe pelas palavras duras, mas já faz dois dias que estou tentando contornar esse comportamento estúpido. A menos que eu estivesse totalmente errado em algum lugar, isso me deixará com a sensação de que algo está realmente quebrado no modelo de pacote e subpacotes do Python.
Notas
- Não quero depender de
sys.path
, para evitar efeitos colaterais globais, nem de*.pth
arquivos, que são apenas outra forma de brincarsys.path
com os mesmos efeitos globais. Para que a solução seja limpa, ela deve ser apenas local. Ou o Python é capaz de lidar com subpacotes, ou não, mas não deveria ser necessário brincar com a configuração global para poder lidar com coisas locais. - Também tentei usar importações no
package/subpackage/__init__.py
, mas não resolveu nada, faz o mesmo, e reclama quesubpackage
não é um módulo conhecido, enquantoprint subpackage
diz que é um módulo (comportamento estranho, de novo).
Pode ser que eu esteja totalmente errado (a opção que eu preferiria), mas isso me deixa muito desapontado com o Python.
Alguma outra forma conhecida além das três que experimentei? Algo que eu não sei?
(suspiro)
-----% <----- editar ----->% -----
Conclusão até agora (após comentários das pessoas)
Não há nada como um subpacote real em Python, já que todas as referências de pacote vão para um dicionário global, apenas, o que significa que não há dicionário local, o que significa que não há maneira de gerenciar a referência de pacote local.
Você deve usar prefixo completo ou prefixo curto ou alias. Como em:
Versão de prefixo completo
from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)
Versão de prefixo curto (mas prefixo repetido)
from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place
Ou então, uma variação do anterior.
from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context
Versão fatorada
Se você não se importa em importar várias entidades de uma vez em um lote, pode:
from package.subpackage.module import attribute1, attribute2
# and etc.
Não está no meu primeiro gosto favorito (prefiro ter uma declaração de importação por entidade importada), mas pode ser aquela que eu pessoalmente prefiro.
Atualização (14/09/2012):
Finalmente parece estar bem na prática, exceto com um comentário sobre o layout. Em vez do acima, usei:
from package.subpackage.module import (
attribute1,
attribute2,
attribute3,
...) # and etc.
__all__
variável que contém uma lista dos nomes que devem ser exportados quando importada estrela. editar: Ok, lendo a resposta de BrenBarn eu entendi o que você quis dizer.Respostas:
Você parece não ter entendido como
import
procura por módulos. Quando você usa uma instrução de importação, ela sempre pesquisa o caminho real do módulo (e / ousys.modules
); ele não faz uso de objetos de módulo no namespace local que existem por causa de importações anteriores. Quando você faz:import package.subpackage.module from package.subpackage import module from module import attribute1
A segunda linha procura um pacote chamado
package.subpackage
e importamodule
desse pacote. Esta linha não tem efeito na terceira linha. A terceira linha apenas procura um módulo chamadomodule
e não encontra nenhum. Ele não "reutiliza" o objeto chamadomodule
que você obteve na linha acima.Em outras palavras
from someModule import ...
, não significa "do módulo denominado someModule que importei anteriormente ..." significa "do módulo denominado someModule que você encontra em sys.path ...". Não há como construir "incrementalmente" o caminho de um módulo importando os pacotes que levam a ele. Você sempre deve referir-se ao nome completo do módulo ao importar.Não está claro o que você está tentando alcançar. Se você deseja apenas importar o objeto particular attribute1, basta fazer
from package.subpackage.module import attribute1
e pronto. Você nunca precisa se preocupar com o longopackage.subpackage.module
depois de importar o nome que deseja dele.Se você não quiser ter acesso ao módulo de acesso outros nomes mais tarde, então você pode fazer
from package.subpackage import module
e, como você viu que você pode então fazermodule.attribute1
e assim por diante, tanto quanto você gosta.Se você deseja ambos - isto é, se deseja ter
attribute1
acesso direto e desejamodule
acessá-lo, basta fazer as duas coisas acima:from package.subpackage import module from package.subpackage.module import attribute1 attribute1 # works module.someOtherAttribute # also works
Se você não gosta de digitar
package.subpackage
nem mesmo duas vezes, pode apenas criar manualmente uma referência local para attribute1:from package.subpackage import module attribute1 = module.attribute1 attribute1 # works module.someOtherAttribute #also works
fonte
module.attribute1
é algo que eu pensei, mas eu pensei que haveria uma maneira de evitar a necessidade de um prefixo em todos os lugares. Portanto, tenho que usar um prefixo em todos os lugares ou criar um alias local, repetindo o nome. Não é o estilo que eu esperava, mas se não tem jeito (afinal, estou acostumado com Ada, que exige algo parecido com suas declarações de renomeação).O motivo da falha # 2 é porque
sys.modules['module']
não existe (a rotina de importação tem seu próprio escopo e não pode ver omodule
nome local) e não hámodule
módulo ou pacote no disco. Observe que você pode separar vários nomes importados por vírgulas.from package.subpackage.module import attribute1, attribute2, attribute3
Além disso:
from package.subpackage import module print module.attribute1
fonte
sys.modules['name']
que eu não sabia até agora, me fez pensar que eu estava com medo (e BrenBarn confirma): não há nada como subpacotes reais em Python.sys.modules
, como seu nome sugere, é global e, se todas as referências a módulos dependem disso, então não há nada como uma referência local a um módulo (pode vir com o Python 3.x?).import
em # 2 gera uma referência local parapackage.subpackage.module
vincularmodule
.Se tudo o que você está tentando fazer é obter attribute1 em seu namespace global, a versão 3 parece adequada. Por que é um prefixo de exagero?
Na versão 2, em vez de
from module import attribute1
você pode fazer
fonte
attribute1 = module.attribute1
está apenas repetindo o nome sem nenhum valor agregado. Sei que funciona, mas não gosto desse estilo (o que não significa que não goste da sua resposta).