Pergunta

Tenho usado Python cada vez mais e continuo vendo a variável __all__ definido em diferentes __init__.py arquivos.Alguém pode explicar o que isso faz?

Foi útil?

Solução

É uma lista de objetos públicos desse módulo, conforme interpretado por import *.Ele substitui o padrão de ocultar tudo que começa com um sublinhado.

Outras dicas

Vinculado, mas não explicitamente mencionado aqui, é exatamente quando __all__ é usado.É uma lista de strings que definem quais símbolos em um módulo serão exportados quando from <module> import * é usado no módulo.

Por exemplo, o seguinte código em um foo.py exporta explicitamente os símbolos bar e baz:

__all__ = ['bar', 'baz']

waz = 5
bar = 10
def baz(): return 'baz'

Esses símbolos podem então ser importados da seguinte forma:

from foo import *

print(bar)
print(baz)

# The following will trigger an exception, as "waz" is not exported by the module
print(waz)

Se o __all__ acima é comentado, este código será executado até a conclusão, conforme o comportamento padrão de import * é importar todos os símbolos que não começam com sublinhado, do namespace fornecido.

Referência: https://docs.python.org/tutorial/modules.html#importing-from-a-package

OBSERVAÇÃO: __all__ afeta o from <module> import * apenas comportamento.Membros que não são mencionados em __all__ ainda estão acessíveis de fora do módulo e podem ser importados com from <module> import <member>.

Estou apenas adicionando isso para ser mais preciso:

Todas as outras respostas referem-se a módulos.A pergunta original mencionou explicitamente __all__ em __init__.py arquivos, então isso é sobre python pacotes.

Geralmente, __all__ só entra em jogo quando o from xxx import * variante do import declaração é usada.Isso se aplica tanto a pacotes quanto a módulos.

O comportamento dos módulos é explicado nas outras respostas.O comportamento exato dos pacotes é descrito aqui em detalhe.

Resumidamente, __all__ no nível do pacote faz aproximadamente a mesma coisa que os módulos, exceto que lida com módulos dentro do pacote (em contraste com a especificação nomes dentro do módulo).Então __all__ especifica todos os módulos que devem ser carregados e importados para o namespace atual quando usarmos from package import *.

A grande diferença é que quando você omitir a declaração de __all__ em um pacote __init__.py, a declaração from package import * não importará nada (com exceções explicadas na documentação, veja o link acima).

Por outro lado, se você omitir __all__ em um módulo, a "importação com estrela" importará todos os nomes (sem começar com sublinhado) definidos no módulo.

Explique __all__ em Python?

Continuo vendo a variável __all__ definido em diferentes __init__.py arquivos.

O que isso faz?

O que __all__ fazer?

Declara os nomes semanticamente "públicos" de um módulo.Se houver um nome em __all__, espera-se que os usuários o utilizem e podem ter a expectativa de que isso não mudará.

Também terá efeitos programáticos:

import *

__all__ em um módulo, por ex. module.py:

__all__ = ['foo', 'Bar']

significa que quando você import * do módulo, apenas os nomes no __all__ são importados:

from module import *               # imports foo and Bar

Ferramentas de documentação

As ferramentas de documentação e preenchimento automático de código também podem (na verdade, devem) inspecionar o __all__ para determinar quais nomes mostrar como disponíveis em um módulo.

__init__.py transforma um diretório em um pacote Python

De documentos:

O __init__.py arquivos são necessários para fazer o Python tratar os diretórios como contendo pacotes;isso é feito para evitar que diretórios com um nome comum, como string, ocultem involuntariamente módulos válidos que ocorrem posteriormente no caminho de pesquisa do módulo.

No caso mais simples, __init__.py pode ser apenas um arquivo vazio, mas também pode executar o código de inicialização do pacote ou definir o __all__ variável.

Então o __init__.py pode declarar o __all__ para pacote.

Gerenciando uma API:

Um pacote normalmente é composto de módulos que podem importar uns aos outros, mas que estão necessariamente vinculados a um __init__.py arquivo.Esse arquivo é o que torna o diretório um pacote Python real.Por exemplo, digamos que você tenha o seguinte:

 package/
   |-__init__.py # makes directory a Python package
   |-module_1.py
   |-module_2.py

no __init__.py você escreve:

from module_1 import *
from module_2 import *

e em module_1 você tem:

__all__ = ['foo',]

e em module_2 você tem:

__all__ = ['Bar',]

E agora você apresentou uma API completa que outra pessoa pode usar ao importar seu pacote, assim:

import package
package.foo()
package.Bar()

E eles não terão todos os outros nomes que você usou ao criar seus módulos, bagunçando o package espaço para nome.

__all__ em __init__.py

Depois de mais trabalho, talvez você tenha decidido que os módulos são muito grandes e precisam ser divididos.Então você faz o seguinte:

 package/
   |-__init__.py
   |-module_1/
   |  |-__init__.py
   |  |-foo_implementation.py
   |-module_2/
      |-__init__.py
      |-Bar_implementation.py

E em cada __init__.py você declara um __all__, por exemplo.no módulo_1:

from foo_implementation import *
__all__ = ['foo']

E módulo_2 __init__.py:

from Bar_implementation import *
__all__ = ['Bar']

E você pode facilmente adicionar coisas à sua API que podem ser gerenciadas no nível do subpacote, em vez do nível do módulo do subpacote.Se você quiser adicionar um novo nome à API, basta atualizar o __init__.py, por exemplo.no módulo_2:

from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']

E se você não estiver pronto para publicar Baz na API de nível superior, em seu nível superior __init__.py você pode ter:

from module_1 import *       # also constrained by __all__'s
from module_2 import *       # in the __init__.py's
__all__ = ['foo', 'Bar']     # further constraining the names advertised

e se seus usuários estiverem cientes da disponibilidade de Baz, eles podem usá-lo:

import package
package.Baz()

mas se eles não souberem disso, outras ferramentas (como pydoc) não os informará.

Você pode alterar isso mais tarde quando Baz está pronto para o horário nobre:

from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']

Prefixando _ contra __all__:

Por padrão, o Python exportará todos os nomes que não começam com um _.Você certamente poderia confiar neste mecanismo.Alguns pacotes da biblioteca padrão do Python, na verdade, fazer dependem disso, mas para fazê-lo, eles alias suas importações, por exemplo, em ctypes/__init__.py:

import os as _os, sys as _sys

Usando o _ a convenção pode ser mais elegante porque elimina a redundância de nomear os nomes novamente.Mas acrescenta redundância para importações (se você tiver muitas delas) e é fácil esquecer de fazer isso de forma consistente - e a última coisa que você quer é ter que suportar indefinidamente algo que você pretendia que fosse apenas um detalhe de implementação, só porque você esqueceu de prefixar um _ ao nomear uma função.

Eu pessoalmente escrevo um __all__ no início do meu ciclo de vida de desenvolvimento de módulos para que outras pessoas que possam usar meu código saibam o que devem ou não usar.

A maioria dos pacotes da biblioteca padrão também usa __all__.

Ao evitar __all__ faz sentido

Faz sentido seguir o _ convenção de prefixo em vez de __all__ quando:

  • Você ainda está no modo de desenvolvimento inicial, não tem usuários e está constantemente ajustando sua API.
  • Talvez você tenha usuários, mas você tem testes de unidade que cobrem a API e ainda está adicionando ativamente à API e ajustando o desenvolvimento.

Um export decorador

A desvantagem de usar __all__ é que você precisa escrever os nomes das funções e classes que estão sendo exportadas duas vezes - e as informações são mantidas separadas das definições.Nós poderia use um decorador para resolver este problema.

Tive a ideia de criar um decorador de exportação na palestra de David Beazley sobre embalagens.Esta implementação parece funcionar bem no importador tradicional do CPython.Se você tiver um gancho ou sistema de importação especial, eu não garanto isso, mas se você adotá-lo, é bastante trivial voltar atrás - você só precisará adicionar manualmente os nomes de volta ao __all__

Então, por exemplo, em uma biblioteca de utilitários, você definiria o decorador:

import sys

def export(fn):
    mod = sys.modules[fn.__module__]
    if hasattr(mod, '__all__'):
        mod.__all__.append(fn.__name__)
    else:
        mod.__all__ = [fn.__name__]
    return fn

e então, onde você definiria um __all__, você faz isso:

$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.

@export
def foo(): pass

@export
def bar():
    'bar'

def main():
    print('main')

if __name__ == '__main__':
    main()

E isso funciona bem, seja executado como principal ou importado por outra função.

$ cat > run.py
import main
main.main()

$ python run.py
main

E provisionamento de API com import * também funcionará:

$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported

$ python run.py
Traceback (most recent call last):
  File "run.py", line 4, in <module>
    main() # expected to error here, not exported
NameError: name 'main' is not defined

Também muda o que o pydoc mostrará:

módulo1.py

a = "A"
b = "B"
c = "C"

módulo2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"

$pydoc módulo1

Help on module module1:

NOME
    module1

ARQUIVO
    module1.py

DADOS
    a = 'A'
    b = 'B'
    c = 'C'

$ módulo pydoc2

Help on module module2:

NOME
    module2

ARQUIVO
    module2.py

DADOS
    __todos__ = ['a', 'b']
    a = 'A'
    b = 'B'

eu declaro __all__ em todos os meus módulos, além de destacar detalhes internos, eles realmente ajudam ao usar coisas que você nunca usou antes em sessões de intérprete ao vivo.

De (Um não oficial) Wiki de referência do Python:

Os nomes públicos definidos por um módulo são determinados verificando o namespace do módulo em busca de uma variável chamada __all__;se definido, deve ser uma sequência de strings que são nomes definidos ou importados por aquele módulo.Os nomes dados em __all__ são todos considerados públicos e devem existir.Se __all__ não estiver definido, o conjunto de nomes públicos inclui todos os nomes encontrados no namespace do módulo que não começam com um caractere de sublinhado ("_"). __all__ deve conter toda a API pública.O objetivo é evitar a exportação acidental de itens que não fazem parte da API (como módulos de biblioteca que foram importados e usados ​​dentro do módulo).

__all__ personaliza o asterisco em from <module> import *

__all__ personaliza o asterisco em from <package> import *


A módulo é um .py arquivo destinado a ser importado.

A pacote é um diretório com um __init__.py arquivo.Um pacote geralmente contém módulos.


MÓDULOS

""" cheese.py - an example module """

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__ permite que os humanos conheçam as características "públicas" de um módulo.[@AaronHall] Além disso, o pydoc os reconhece.[@Longpoke]

de módulo importar *

Veja como swiss e cheddar são trazidos para o namespace local, mas não gouda:

>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined

Sem __all__, qualquer símbolo (que não comece com sublinhado) estaria disponível.


Importações sem * não são afetados por __all__


importar módulo

>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)

de módulo importar nomes

>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)

importar módulo como nome local

>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)

PACOTES

No __init__.py arquivo de um pacote __all__ é uma lista de strings com nomes de módulos públicos ou outros objetos.Esses recursos estão disponíveis para importações curinga.Tal como acontece com os módulos, __all__ personaliza o * ao importar curinga do pacote.[@MartinStettner]

Aqui está um trecho do Conector Python MySQL __init__.py:

__all__ = [
    'MySQLConnection', 'Connect', 'custom_error_exception',

    # Some useful constants
    'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
    'HAVE_CEXT',

    # Error handling
    'Error', 'Warning',

    ...etc...

    ]

O caso padrão, asterisco sem __all__ por um pacote, é complicado, porque o comportamento óbvio seria caro:para usar o sistema de arquivos para procurar todos os módulos do pacote.Em vez disso, na minha leitura dos documentos, apenas os objetos definidos em __init__.py são importados:

Se __all__ não está definido, a declaração from sound.effects import * faz não importe todos os submódulos do pacote sound.effects no namespace atual;apenas garante que o pacote sound.effects foi importado (possivelmente executando qualquer código de inicialização em __init__.py) e depois importa quaisquer nomes definidos no pacote.Isso inclui quaisquer nomes definidos (e submódulos carregados explicitamente) por __init__.py.Também inclui quaisquer submódulos do pacote que foram carregados explicitamente por instruções de importação anteriores.


Importações curinga...devem ser evitados porque [confundem] os leitores e muitas ferramentas automatizadas.

[PEP 8, @ToolmakerSteve]

Resposta curta

__all__ afeta from <module> import * declarações.

Resposta longa

Considere este exemplo:

foo
├── bar.py
└── __init__.py

Em foo/__init__.py:

  • (Implícito) Se não definirmos __all__, então from foo import * importará apenas nomes definidos em foo/__init__.py.

  • (Explícito) Se definirmos __all__ = [], então from foo import * não importará nada.

  • (Explícito) Se definirmos __all__ = [ <name1>, ... ], então from foo import * importará apenas esses nomes.

Observe que no caso implícito, python não importará nomes começando com _.No entanto, você pode forçar a importação de tais nomes usando __all__.

Você pode visualizar o documento Python aqui.

__all__ é usado para documentar a API pública de um módulo Python.Embora seja opcional, __all__ deve ser usado.

Aqui está o trecho relevante de a referência da linguagem Python:

Os nomes públicos definidos por um módulo são determinados verificando o namespace do módulo em busca de uma variável chamada __all__;se definido, deve ser uma sequência de strings que são nomes definidos ou importados por aquele módulo.Os nomes dados em __all__ são todos considerados públicos e devem existir.Se __all__ não estiver definido, o conjunto de nomes públicos inclui todos os nomes encontrados no namespace do módulo que não começam com um caractere de sublinhado ('_'). __all__ deve conter toda a API pública.O objetivo é evitar a exportação acidental de itens que não fazem parte da API (como módulos de biblioteca que foram importados e usados ​​dentro do módulo).

PEP 8 usa redação semelhante, embora também deixe claro que os nomes importados não fazem parte da API pública quando __all__ está ausente:

Para melhor apoiar a introspecção, os módulos devem declarar explicitamente os nomes em sua API pública usando o __all__ atributo.Contexto __all__ para uma lista vazia indica que o módulo não possui API pública.

[...]

Os nomes importados devem sempre ser considerados um detalhe de implementação.Outros módulos não devem depender de acesso indireto a tais nomes importados, a menos que sejam uma parte explicitamente documentada da API do módulo que os contém, como os.path ou um pacote __init__ módulo que expõe funcionalidade de submódulos.

Além disso, como apontado em outras respostas, __all__ é usado para ativar importação de curinga para pacotes:

A instrução import usa a seguinte convenção:se um pacote for __init__.py código define uma lista chamada __all__, é considerada a lista de nomes de módulos que devem ser importados quando from package import * é encontrado.

Além das respostas existentes, __all__ não precisa ser uma lista.De acordo com a documentação do import declaração, se definido, __all__ deve ser um sequência de cordas que são nomes definidos ou importados pelo módulo.Então você também pode usar uma tupla para salvar alguns ciclos de memória e CPU.Só não esqueça da vírgula caso o módulo defina um único nome público:

__all__ = ('some_name',)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top