Adicionando código a __init__.py

85

Estou dando uma olhada em como funciona o sistema de modelos no django e percebi algo que não entendo.

Eu sei que você cria um __init__.pyarquivo vazio para especificar que o diretório atual é um pacote. E que você pode definir alguma variável __init__.pypara que import * funcione corretamente.

Mas django adiciona um monte de instruções from ... import ... e define um monte de classes em __init__.py. Por quê? Isso não faz com que as coisas pareçam confusas? Existe um motivo que requer este código __init__.py?

Erik
fonte
13
Isso não é realmente sobre Django, é? Sim, você viu primeiro no Django, mas parece mais uma coisa pura do Python - talvez a tag Django não seja realmente apropriada.
S.Lott
Não vejo nenhuma declaração de importação no __init__.pydjango 1.8. Era para uma versão mais antiga? se sim qual versão?
Gobi Dasu

Respostas:

72

Todas as importações em __init__.pysão disponibilizadas quando você importa o pacote (diretório) que o contém.

Exemplo:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

EDIT: esqueci de mencionar, o código __init__.pyé executado na primeira vez que você importa qualquer módulo desse diretório. Portanto, normalmente é um bom lugar para colocar qualquer código de inicialização no nível do pacote.

EDIT2: dgrant apontou para uma possível confusão no meu exemplo. Em __init__.py import somethingpode importar qualquer módulo, não é necessário a partir do pacote. Por exemplo, podemos substituí-lo por import datetime, então, em nosso nível superior, test.pyesses dois snippets funcionarão:

import dir
print dir.datetime.datetime.now()

e

import dir.some_module_in_dir
print dir.datetime.datetime.now()

O resultado final é: todos os nomes atribuídos __init__.py, sejam módulos importados, funções ou classes, estão automaticamente disponíveis no namespace do pacote sempre que você importar o pacote ou um módulo do pacote.

Alexander Kojevnikov
fonte
Ok, obrigado. Mas ainda não tenho certeza de por que seria uma boa ideia adicionar classes ao __init__.py Eu realmente não considero o código de inicialização dessas classes (mas talvez eu esteja errado sobre isso).
Erik
Essas são provavelmente as classes úteis cada vez que você trabalha com o pacote. Mas não quero especular, pode haver muitas razões pelas quais eles estão lá, objetivos ou não :)
Alexander Kojevnikov
13
Isso também pode ser por razões históricas. Quando você está convertendo módulo em pacote, module.py em module / __ init__.py todo o código existente pode usá-lo como antes, mas agora o módulo pode ter submódulos.
Łukasz
1
Os módulos executam o pai __init__.pyimplicitamente. Ao importar os módulos internos __init__.py, você está criando importações cíclicas. O __init__.pynão será executado totalmente antes de tal importação. É mais seguro ficar __init__.pyvazio.
Ivo Danihelka
É importante observar que isso não é específico para __init__.pyarquivos. Se você tivesse um arquivo dir/other.pyque tivesse algo parecido com from datetime import datetimevocê também seria capaz de ligar dir.other.datetime.now()ou até mesmo from dir.other import datetime.
Carles Sala
37

É apenas uma preferência pessoal, na verdade, e tem a ver com o layout de seus módulos Python.

Digamos que você tenha um módulo chamado erikutils. Pode ser um módulo de duas maneiras: você tem um arquivo chamado erikutils.py em seu computadorsys.path ou um diretório chamado erikutils em seu sys.pathcom um __init__.pyarquivo vazio dentro dele. Então, digamos que você tem um monte de módulos chamados fileutils, procutils, parseutilse você quer aqueles para ser sub-módulos menores erikutils. Então, você faz alguns arquivos .py chamados fileutils.py , procutils.py e parseutils.py :

erikutils
  __init__.py
  fileutils.py
  procutils.py
  parseutils.py

Talvez você tem algumas funções que simplesmente não pertencem nos fileutils, procutilsou parseutilsmódulos. E digamos que você não queira criar um novo módulo chamado miscutils. E, você gostaria de poder chamar a função assim:

erikutils.foo()
erikutils.bar()

ao invés de fazer

erikutils.miscutils.foo()
erikutils.miscutils.bar()

Portanto, como o erikutilsmódulo é um diretório, não um arquivo, temos que definir suas funções dentro do __init__.pyarquivo.

Em django, o melhor exemplo que posso pensar é django.db.models.fields. TODAS as classes django * Field são definidas no __init__.pyarquivo no diretório django / db / models / fields . Eu acho que eles fizeram isso porque não queria empinar tudo em uma hipotética / db / modelos / fields.py django modelo, para que eles dividi-lo para fora em algumas sub-módulos ( related.py , files.py , por exemplo) e eles colaram as definições de * Field feitas no próprio módulo de campos (portanto, __init__.py).

dgrant
fonte
1
dgrant, o que eu quis dizer é que somethingpode ser um módulo externo, dir. algo ainda funcionará. Obrigado pelo comentário, irei editar meu post para deixar mais claro.
Alexander Kojevnikov
29

Usar o __init__.pyarquivo permite que você torne a estrutura interna do pacote invisível de fora. Se a estrutura interna mudar (por exemplo, porque você dividiu um módulo de gordura em dois), você só precisa ajustar o__init__.py arquivo, mas não o código que depende do pacote. Você também pode tornar partes do seu pacote invisíveis, por exemplo, se não estiverem prontas para uso geral.

Observe que você pode usar o delcomando, então um típico __init__.pypode ser assim:

from somemodule import some_function1, some_function2, SomeObject

del somemodule

Agora, se você decidir dividir, somemoduleo novo __init__.pypode ser:

from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject

del somemodule1
del somemodule2

Do lado de fora, a embalagem ainda parece exatamente como antes.

Nikow
fonte
1
@Arlen: A questão é que não faz parte da API pública. Se você renomear um módulo, pode ter certeza de que nenhum código dependente será quebrado. Além disso, isso garante que os elementos da API apareçam apenas uma vez, por exemplo, quando a introspecção é usada para criar a documentação da API automaticamente.
nikow
5
@Arlen: Excluir um módulo impede que ele seja import <pack>.somemodule1diretamente. Você só pode importar de <pack>objetos definidos ou importados em seus __init__.pysubmódulos e submódulos não excluídos.
MestreLion