__all__ em Python
À medida que nos aprofundamos nos pacotes e módulos, podemos encontrar a variável __all__
definida em diferentes arquivos _init_.py
.
Os arquivos __init__.py
são os arquivos que fazem o Python tratar os diretórios como alguns pacotes contendo. Esse arquivo evita que diretórios com nomes semelhantes, como cadeias de caracteres, ocultem módulos válidos que podem ocorrer posteriormente em um caminho de pesquisa de módulo.
No caso mais simples, __init__.py
pode ser um arquivo vazio, mas também pode executar o código de inicialização para o pacote ou definir a variável __all__
.
Portanto, o __init__.py
pode declarar as variáveis __all__
para um pacote.
Uma lista de objetos públicos desse módulo é fornecida na variável __all__
. É interpretado por import *
. Esta variável substitui o padrão de ocultar tudo o que começa com um sublinhado do namespace fornecido.
Por exemplo,
__all__ = ["a", "b"]
c = 5
a = 10
def b():
return "b"
Agora importamos isso no código a seguir.
from sample import *
print(a) # will work fine
print(b) # will work fine
print(c) # will generate an error
No exemplo acima, usamos import *
para importar todos os objetos públicos do arquivo sample.py
para este arquivo. Isso significa que este arquivo importará e suportará todos os objetos públicos do arquivo sample.py
.
Os objetos a
e b
serão importados e o novo código funcionará perfeitamente onde esses objetos são usados. O problema surge ao usar o terceiro objeto, c
. Esse objeto nunca é importado para o novo arquivo, pois não é um objeto público, pois não faz parte da variável __all__
. Portanto, esta parte do código irá gerar um erro.
Existe uma alternativa para isso. Por padrão, o Python terá a responsabilidade de exportar todos os nomes que não comecem com um sublinhado _
. E certamente se poderia contar com esse mecanismo. Na biblioteca padrão do Python, alguns pacotes contam com isso, mas para fazer isso, eles apelidam suas importações, por exemplo, os
como _os
, sys
como _sys
, etc.
Usar a convenção _
é mais prático, pois remove a redundância de nomear os nomes repetidamente. Mas adiciona redundância para importações (se houver muitas) e é mais fácil esquecer de fazer isso de forma consistente.
Muitos pacotes da biblioteca padrão usam __all__
. Faz sentido usar a convenção de prefixo _
no lugar de __all__
quando ainda está no modo de desenvolvimento inicial e não tem usuários e está constantemente ajustando sua API. Talvez haja alguns usuários, mas um tem testes de unidade cobrindo a API e ainda atualizando ativamente e adicionando à API e ajustes no desenvolvimento.