Às vezes, torna-se necessário separar vários pacotes no mesmo espaço para nome em diferentes caminhos físicos. Por exemplo, se você deseja transferir diferentes layouts de plug-ins, pode adicioná-los posteriormente sem controlar sua localização e, ao mesmo tempo, acessá-los através de um espaço para nome.
Esta folha de dicas, mais adequada para iniciantes, é dedicada aos namespaces do Python.
Vejamos como isso pode ser feito em diferentes versões do Python, pois, embora o Python2 não seja mais suportado em breve, muitos de nós estamos neste momento entre dois incêndios, e essa é apenas uma das nuances importantes na transição.

Considere este exemplo:
Queremos obter a estrutura do pacote:
namespace1 package1 module1 package2 module2
Conteúdo do arquivo Module1
print('package 1') var1 = 1
Conteúdo do arquivo Module2
print('package 2') var2 = 2
Ao mesmo tempo, os pacotes são distribuídos na seguinte estrutura de pastas:
path1 namespace1 package1 module1 path2 namespace1 package2 module2
Suponha que, de alguma forma, path1 e path2 já tenham sido adicionados ao sys.path. Precisamos acessar module1 e module2:
from namespace1.package1 import module1 from namespace1.package2 import module2
O que acontece no
Python 3.7 quando esse código é executado? Tudo funciona maravilhosamente:
package 1 package 2
Com o PEP-420 no Python 3.3, o suporte para namespaces implícitos apareceu. Além disso, ao importar um pacote do py33, você não precisa criar arquivos __init__.py. E ao importar namespace, é apenas _ proibido. Se o arquivo __init__.py estiver presente em um ou nos dois diretórios com o nome name1, ocorrerá um erro na importação do segundo pacote.
ModuleNotFoundError: No module named 'namespace1.package2'
Assim, a presença de alguém interno determina explicitamente o pacote, e os pacotes não podem ser combinados, é uma entidade única. Se você estiver iniciando um novo projeto, independente do desenvolvimento antigo, e os pacotes forem instalados usando o pip, será necessário seguir esse método. No entanto, às vezes herdamos o código antigo, que também precisa ser mantido, pelo menos por um tempo, ou portado para uma nova versão.
Vamos para o
Python 2.7 . Com esta versão já é mais interessante, primeiro você precisa adicionar __init__.py a cada diretório para criar pacotes, caso contrário, o intérprete simplesmente não reconhece o pacote nesse conjunto de arquivos. E, em seguida, escreva a declaração explícita do espaço para nome nos arquivos __init__ relacionados ao espaço para nome1; caso contrário, apenas o primeiro pacote será importado.
from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
O que acontece com isso? Quando o intérprete atinge a primeira importação, um pacote com o mesmo nome é pesquisado em sys.path, ele está no caminho1 / namespace1 e o interpretador executa o caminho1 / namespace1 / __ init__.py. Não são realizadas pesquisas adicionais. No entanto, a própria função extend_path realiza uma pesquisa em todo sys.path, localiza todos os pacotes com o nome namespace1 e o nome interno e os adiciona à variável __path__ do pacote namespace1, usada para procurar pacotes filho nesse namespace.
Nos guias oficiais, é recomendável que as iniciais sejam as mesmas sempre que o namespace1 for colocado. De fato, eles podem estar vazios, exceto o primeiro, que é encontrado durante uma pesquisa em sys.path, na qual pkgutil.extend_path deve ser chamado, porque o restante não é executado. No entanto, é claro, é melhor que a chamada seja realmente verdadeira em todas as equipes, para não amarrar sua lógica "no caso" e não adivinhar qual equipe foi a primeira a executar, porque a ordem de pesquisa pode mudar. Pelo mesmo motivo, você não deve colocar outros arquivos lógicos __init__ na área variável.
Isso funcionará em versões futuras e esse código pode ser usado para escrever um
código compatível , mas lembre-se de que você deve aderir ao método escolhido em todos os pacotes distribuídos. Se na versão 3 você colocar uma caixa de entrada em alguns pacotes em uma chamada para pkgutil.extend_path e deixar alguns sem uma caixa de entrada, isso não funcionará.
Além disso, essa opção também é adequada para o caso em que você planeja instalar usando o python setup.py install.
Outra maneira, que agora é considerada um pouco desatualizada, mas ainda pode ser encontrada muito onde:
O módulo pkg_resources vem com o pacote setuptools. Aqui, o significado é o mesmo que no pkgutil - é necessário que cada arquivo __init__ contenha a mesma declaração de espaço para nome em todos os locais do espaço para nome1 e nenhum outro código esteja presente. Ao mesmo tempo, é necessário registrar o espaço para nome namespace_packages = ['namespace1'] em setup.py. Uma descrição mais detalhada da criação de pacotes está além do escopo deste artigo.
Além disso, você pode frequentemente encontrar esse código
try: __import__('pkg_resources').declare_namespace(__name__) except: from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
Aqui a lógica é simples - se o setuptools não estiver instalado, usamos o pkgutil, que está incluído na biblioteca padrão.
Se você configurar um espaço para nome de uma dessas maneiras, poderá chamar outro de um módulo. Por exemplo, altere namespace1 / package2 / module2
import namespace1.package1.module1 print(var1)
E então veremos o que acontece se nomearmos por engano um novo pacote e um pacote existente e o envolvermos com o mesmo espaço de nome. Por exemplo, haverá dois pacotes em locais diferentes com o nome package1.
namespace1 package1 module1 package1 module2
Nesse caso, apenas o primeiro será importado e não haverá acesso ao módulo2. Pacotes não podem ser combinados.
from namespace1.package1 import module1 from namespace1.package1 import module2
Resumo:- Para Python anterior a 3.3 e instalando com pip, é recomendável usar uma declaração implícita de namespace.
- No caso de suporte para as versões 2 e 3, bem como a instalação com o pip e python setup.py install, a opção com pkgutil é recomendada.
- A opção pkg_resources é recomendada se você precisar dar suporte a pacotes mais antigos usando esse método ou se o pacote deve ser protegido por zip.
Fontes:
Exemplos podem ser encontrados
aqui .