Use 'import module' ou 'from module import'?

411

Tentei encontrar um guia abrangente sobre se é melhor usar import moduleou from module import? Acabei de começar com Python e estou tentando começar com as melhores práticas em mente.

Basicamente, eu estava esperando se alguém poderia compartilhar suas experiências, o que as preferências de outros desenvolvedores têm e qual é a melhor maneira de evitar armadilhas no caminho?

Filip Dupanović
fonte
5
Eu só queria que você soubesse que a resposta selecionada está errada. Ele afirma que a diferença é subjetiva, ao passo que há uma diferença. Isso pode levar a erros difíceis de detectar. Veja a resposta de Michael Ray Lovetts.
Mayou36
2
Não é um inferno de uma diferença entre importar identificadores chamados específicos 'from module import X,Y,Zvs'from module import * . Este último polui seu espaço para nome e pode fornecer resultados imprevisíveis, dependendo do que está acontecendo no módulo. O pior ainda está acontecendo from module import *com vários módulos.
smci 15/09/19

Respostas:

474

A diferença entre import modulee from module import fooé principalmente subjetiva. Escolha o que você mais gosta e seja consistente em seu uso. Aqui estão alguns pontos para ajudá-lo a decidir.

import module

  • Prós:
    • Menos manutenção de suas importdeclarações. Não é necessário adicionar importações adicionais para começar a usar outro item do módulo
  • Contras:
    • Digitar module.fooseu código pode ser entediante e redundante (o tédio pode ser minimizado usando import module as moe digitando mo.foo)

from module import foo

  • Prós:
    • Menos digitação para usar foo
    • Mais controle sobre quais itens de um módulo podem ser acessados
  • Contras:
    • Para usar um novo item do módulo, você deve atualizar sua importdeclaração
    • Você perde o contexto foo. Por exemplo, é menos claro o que ceil()faz em comparação commath.ceil()

Qualquer um dos métodos é aceitável, mas não use from module import *.

Para qualquer conjunto grande de códigos razoável, se você import *provavelmente o cimentará no módulo, não poderá ser removido. Isso ocorre porque é difícil determinar quais itens usados ​​no código são provenientes de 'module', facilitando chegar ao ponto em que você acha que não usa importmais, mas é extremamente difícil ter certeza.

Mark Roddy
fonte
66
+1 para desencorajar o uso de "from module import *", apenas desorganiza o espaço para nome.
7897 Christian Witts
22
desordenar o espaço para nome não é a parte mais problemática da "importação *", é a redução da legibilidade: qualquer conflito de nome aparecerá no teste (de unidade). Mas todos os nomes que você usa do módulo importado estarão vazios, com nenhuma dica de onde eles vieram. Eu detesto absolutamente "importar *".
Jürgen A. Erhard,
21
O Zen do Python não diz que explícito é melhor do que implícito?
Antony Koch
8
from module import *pode ser particularmente útil, se usá-lo como: if(windows):\n\t from module_win import * \n else: \n\t from module_lin import *. Em seguida, seu módulo pai pode potencialmente conter nomes de funções independentes do SO, se os nomes de função em module_lin e module_win tiverem os mesmos nomes. É como herdar condicionalmente qualquer uma das classes.
anishsane
19
@anishsane. Existe outra maneira de fazê-lo. importe module_win como algo. Em seguida, sempre use something.method_name ()
Vinay
163

Há outro detalhe aqui, não mencionado, relacionado à gravação em um módulo. Concedido isso pode não ser muito comum, mas eu preciso disso de tempos em tempos.

Devido à maneira como as referências e a associação de nomes funcionam no Python, se você deseja atualizar algum símbolo em um módulo, diga foo.bar, de fora desse módulo, e tenha outro código de importação "veja" essa alteração, você deve importar certo caminho. Por exemplo:

módulo foo:

bar = "apples"

módulo a:

import foo
foo.bar = "oranges"   # update bar inside foo module object

módulo b:

import foo           
print foo.bar        # if executed after a's "foo.bar" assignment, will print "oranges"

No entanto, se você importar nomes de símbolos em vez de nomes de módulos, isso não funcionará.

Por exemplo, se eu fizer isso no módulo a:

from foo import bar
bar = "oranges"

Nenhum código fora de a verá a barra como "laranjas" porque minha configuração de barra afetou apenas o nome "barra" dentro do módulo a, não "alcançou" o objeto do módulo foo e atualizou sua "barra".

Michael Ray Lovett
fonte
Com esse último exemplo, você ainda pode chamar 'foo.bar = "orange"' para atualizar 'bar' dentro de 'foo'?
Velocirabbit
4
Não, no último exemplo, o nome 'foo' é desconhecido
Ghislain Leveque 11/01
31
Esta resposta fornece a resposta "verdadeiro" sobre a questão: qual é a diferença entre os dois importação variantes
Mayou36
3
Escreveu algum trecho para provar que esta resposta é absolutamente correta, mas qual é a lógica por trás disso?
huangbeidu
Eu acho que o que você está dizendo é importar nomes de símbolos para ter variáveis ​​locais, mas importar nomes de módulos para ter variáveis ​​globais ???
WinEunuuchs2Unix
79

Mesmo que muitas pessoas já tenham explicado sobre o importvs import from, quero tentar explicar um pouco mais sobre o que acontece sob o capô e onde estão todos os lugares em que ele muda.


import foo:

Importa fooe cria uma referência a esse módulo no espaço para nome atual. Em seguida, você precisa definir o caminho completo do módulo para acessar um atributo ou método específico de dentro do módulo.

Por exemplo, foo.barmas nãobar

from foo import bar:

Importa fooe cria referências a todos os membros listados ( bar). Não define a variável foo.

Por exemplo, barmas não bazoufoo.baz

from foo import *:

Importa fooe cria referências a todos os objetos públicos definidos por esse módulo no espaço para nome atual (tudo listado __all__se __all__existir, caso contrário, tudo o que não começa _). Não define a variável foo.

Por exemplo, bare bazmas não _quxou foo._qux.


Agora vamos ver quando fazemos import X.Y:

>>> import sys
>>> import os.path

Verifique sys.modulescom nome ose os.path:

>>> sys.modules['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>

Os dicionários de verificação globals()e locals()namespace com ose os.path:

 >>> globals()['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> locals()['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> globals()['os.path']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'os.path'
>>>

A partir do exemplo acima, descobrimos que apenas osé inserido no espaço para nome local e global. Portanto, devemos poder usar:

 >>> os
 <module 'os' from
  '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
 >>> os.path
 <module 'posixpath' from
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
 >>>

Mas não path.

>>> path
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
>>>

Depois de excluir o osespaço de nomes from locals (), você não poderá acessar os, os.pathmesmo que eles existam em sys.modules:

>>> del locals()['os']
>>> os
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>> os.path
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>

Agora vamos falar sobre import from:

from:

>>> import sys
>>> from os import path

Verifique sys.modulescom ose os.path:

>>> sys.modules['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>

Descobrimos que em sys.modulesque encontramos o mesmo que antes, usandoimport name

OK, cheque de deixar como ele se parece em locals()e globals()namespace dicts:

>>> globals()['path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> locals()['path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> globals()['os']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'os'
>>>

Você pode acessar usando o nome pathnão por os.path:

>>> path
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> os.path
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>

Vamos excluir 'path' de locals():

>>> del locals()['path']
>>> path
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
>>>

Um exemplo final usando um alias:

>>> from os import path as HELL_BOY
>>> locals()['HELL_BOY']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> globals()['HELL_BOY']
<module 'posixpath' from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>>

E nenhum caminho definido:

>>> globals()['path']
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
KeyError: 'path'
>>>
James Sapam
fonte
8
Embora isso seja detalhado, essa é realmente a melhor resposta da lista para uma pergunta bastante complexa. Ele fornece código real para ajudar a explicar as sutilezas "ocultas", que são mais importantes que o estilo, para esse problema em particular. Eu gostaria de poder votar mais de uma vez!
Mike Williamson
O uso as SYMBOLmuda como essa resposta funciona?
Maximilian Burszley 29/01
40

As duas formas são suportadas por um motivo: há momentos em que uma é mais apropriada que a outra.

  • import module: nice quando você estiver usando muitos bits do módulo. A desvantagem é que você precisará qualificar cada referência com o nome do módulo.

  • from module import ...: bom que itens importados sejam utilizáveis ​​diretamente sem o prefixo do nome do módulo. A desvantagem é que você deve listar cada coisa que usa e que não está claro no código de onde algo veio.

Qual usar depende do que torna o código claro e legível e tem mais do que um pouco de preferência pessoal. Eu me inclino em import modulegeral, porque no código é muito claro de onde veio um objeto ou função. Eu uso from module import ...quando estou usando algum objeto / função de um monte no código.

dwc
fonte
1
Existe uma maneira de usar from M import Xe ainda obter o benefício de usar os qualificadores de alguma forma? Parece que você poderia obter o melhor dos dois mundos se ainda pudesse fazer M.Xisso após a importação.
Artrópode
@artgropod: Meio. Você pode fazer class m: from something.too.long import x, y, z. Realmente não recomendaria isso.
Lie Ryan
35

Eu pessoalmente sempre uso

from package.subpackage.subsubpackage import module

e depois acessar tudo como

module.function
module.modulevar

etc. O motivo é que, ao mesmo tempo, você tem uma chamada curta e define claramente o espaço para nome do módulo de cada rotina, algo que é muito útil se você precisar procurar o uso de um determinado módulo em sua fonte.

Escusado será dizer que não use a importação *, pois polui seu espaço de nome e não informa de onde vem uma determinada função (de qual módulo)

Obviamente, você pode ter problemas se tiver o mesmo nome de módulo para dois módulos diferentes em dois pacotes diferentes, como

from package1.subpackage import module
from package2.subpackage import module

Nesse caso, é claro que você se depara com problemas, mas há uma forte dica de que o layout do seu pacote é defeituoso e é necessário repensá-lo.

Stefano Borini
fonte
10
No último caso, você sempre pode usar: import pkgN.sub.module como modN, fornecendo nomes distintos para cada módulo. Você também pode usar o padrão 'import modulename as mod1' para encurtar um nome longo ou alternar entre implementações da mesma API (por exemplo, módulos da API DB) com uma única alteração de nome.
21119 Jeff Shannon
15
import module

É melhor quando você usará muitas funções do módulo.

from module import function

É melhor quando você deseja evitar poluir o espaço para nome global com todas as funções e tipos de um módulo quando você só precisa function.

Andrew Hare
fonte
7
Certamente, a única coisa no espaço de nomes global se você 'importar módulo' é 'módulo'? Você só polui o espaço para nome se fizer 'from .. import *'.
John Fouhy
10

Acabei de descobrir mais uma diferença sutil entre esses dois métodos.

Se o módulo foousa uma seguinte importação:

from itertools import count

O módulo barpode, por engano, usar countcomo se tivesse sido definido em foo, não em itertools:

import foo
foo.count()

Se foousa:

import itertools

o erro ainda é possível, mas menos provável de ser cometido. barprecisa de:

import foo
foo.itertools.count()

Isso me causou alguns problemas. Eu tinha um módulo que, por engano, importou uma exceção de um módulo que não o definiu, apenas o importou de outro módulo (usando from module import SomeException). Quando a importação não era mais necessária e removida, o módulo incorreto foi interrompido.

Jan Wrobel
fonte
10

Aqui está outra diferença não mencionada. Isso é copiado literalmente em http://docs.python.org/2/tutorial/modules.html

Observe que ao usar

from package import item

o item pode ser um submódulo (ou subpacote) do pacote ou outro nome definido no pacote, como uma função, classe ou variável. A instrução de importação primeiro testa se o item está definido no pacote; caso contrário, assume que é um módulo e tenta carregá-lo. Se não conseguir encontrá-lo, uma exceção ImportError será gerada.

Ao contrário, ao usar sintaxe como

import item.subitem.subsubitem

cada item, exceto o último, deve ser um pacote; o último item pode ser um módulo ou um pacote, mas não pode ser uma classe ou função ou variável definida no item anterior.

user2141737
fonte
Outra coisa que notei foi que, se item também é um submódulo dentro do pacote, o item "from package import item" funciona, mas o "import package" package.item.subitem = ... não funciona com um init .py do pacote vazio , a menos que tem "item de importação" no arquivo init do pacote.
Amitoz Dandiana
6

Como também sou iniciante, tentarei explicar isso de uma maneira simples: No Python, temos três tipos de importdeclarações que são:

1. Importações genéricas:

import math

esse tipo de importação é meu favorito pessoal, a única desvantagem dessa técnica de importação é que, se você precisar usar a função de qualquer módulo, deverá usar a seguinte sintaxe:

math.sqrt(4)

é claro, aumenta o esforço de digitação, mas como iniciante, ajudará você a acompanhar o módulo e as funções associadas a ele (um bom editor de texto reduzirá significativamente o esforço de digitação e é recomendado).

O esforço de digitação pode ser reduzido ainda mais usando esta declaração de importação:

import math as m

agora, em vez de usar, math.sqrt()você pode usar m.sqrt().

2. Importações de funções:

from math import sqrt

esse tipo de importação é mais adequado se o seu código precisar acessar apenas uma ou poucas funções do módulo, mas para usar qualquer novo item do módulo, é necessário atualizar a instrução de importação.

3. Importações universais:

from math import * 

Embora reduza significativamente o esforço de digitação, mas não é recomendado, pois ele preencherá seu código com várias funções do módulo e seu nome poderá entrar em conflito com o nome das funções definidas pelo usuário. exemplo:

Se você possui uma função do seu próprio sqrt nomeado e importa matemática, sua função é segura: existe o sqrt e o math.sqrt. Se você fizer a importação matemática *, no entanto, você terá um problema: duas funções diferentes com o mesmo nome. Fonte: Codecademy

Shashank Rawat
fonte
5
import package
import module

Com import, o token deve ser um módulo (um arquivo que contém comandos Python) ou um pacote (uma pasta que sys.pathcontém um arquivo __init__.py).

Quando há subpacotes:

import package1.package2.package
import package1.package2.module

os requisitos para pasta (pacote) ou arquivo (módulo) são os mesmos, mas a pasta ou o arquivo deve estar dentro do package2qual deve estar dentro package1e ambos package1e package2devem conter __init__.pyarquivos. https://docs.python.org/2/tutorial/modules.html

Com o fromestilo de importação:

from package1.package2 import package
from package1.package2 import module

o pacote ou módulo entra no espaço para nome do arquivo que contém a importinstrução como module(ou package) em vez de package1.package2.module. Você sempre pode vincular a um nome mais conveniente:

a = big_package_name.subpackage.even_longer_subpackage_name.function

Somente o fromestilo de importação permite nomear uma função ou variável específica:

from package3.module import some_function

é permitido, mas

import package3.module.some_function 

não é permitido.

Bennett Brown
fonte
4

Para acrescentar ao que as pessoas disseram from x import *: além de tornar mais difícil saber de onde os nomes vieram, isso desencadeia verificadores de código como o Pylint. Eles reportarão esses nomes como variáveis ​​indefinidas.

DNS
fonte
3

Minha própria resposta a isso depende principalmente de quantos módulos diferentes eu vou usar. Se eu vou usar apenas um ou dois, usarei frequentemente from... importuma vez que gera menos pressionamentos de tecla no restante do arquivo, mas se vou usar muitos módulos diferentes, prefiro apenas importporque isso significa que cada referência de módulo é auto-documentada. Eu posso ver de onde vem cada símbolo sem ter que caçar.

Geralmente eu prefiro o estilo de auto-documentação de importação simples e só mudo para de .. import quando o número de vezes que eu tenho que digitar o nome do módulo cresce acima de 10 a 20, mesmo se houver apenas um módulo sendo importado.

SingleNegationElimination
fonte
1

Uma das diferenças significativas que descobri, que surpreendentemente ninguém falou, é que usando a importação simples você pode acessar private variablee private functionsdo módulo importado, o que não é possível com a declaração from-import .

insira a descrição da imagem aqui

Código na imagem:

setting.py

public_variable = 42
_private_variable = 141
def public_function():
    print("I'm a public function! yay!")
def _private_function():
    print("Ain't nobody accessing me from another module...usually")

plain_importer.py

import settings
print (settings._private_variable)
print (settings.public_variable)
settings.public_function()
settings._private_function()

# Prints:
# 141
# 42
# I'm a public function! yay!
# Ain't nobody accessing me from another module...usually

from_importer.py

from settings import *
#print (_private_variable) #doesn't work
print (public_variable)
public_function()
#_private_function()   #doesn't work
Saurav Sahu
fonte
0

Importar módulo - você não precisa de esforços adicionais para buscar outra coisa do módulo. Tem desvantagens, como digitação redundante

Importação de módulos de - menos digitação e mais controle sobre quais itens de um módulo podem ser acessados. Para usar um novo item do módulo, é necessário atualizar sua declaração de importação.

Dlucidona
fonte
0

Existem alguns módulos internos que contêm principalmente funções simples ( base64 , matemática , sistema operacional , shutil , sys , time , ...) e é definitivamente uma boa prática vincular essas funções a algum espaço de nome e, assim, melhorar a legibilidade do seu código. Considere o quão mais difícil é entender o significado dessas funções sem o namespace:

copysign(foo, bar)
monotonic()
copystat(foo, bar)

do que quando eles estão vinculados a algum módulo:

math.copysign(foo, bar)
time.monotonic()
shutil.copystat(foo, bar)

Às vezes, você precisa do espaço para nome para evitar conflitos entre diferentes módulos ( json.load vs. pickle.load )


Por outro lado, existem alguns módulos que contêm principalmente classes ( ConfigParser , datetime , tempfile , zipfile , ...) e muitos deles fazem seus nomes de classe bastante auto-explicativo:

configparser.RawConfigParser()
datetime.DateTime()
email.message.EmailMessage()
tempfile.NamedTemporaryFile()
zipfile.ZipFile()

portanto, pode haver um debate sobre se o uso dessas classes com o espaço de nome do módulo adicional no seu código adiciona novas informações ou apenas aumenta o código.

Jeyekomon
fonte
0

Gostaria de acrescentar que há algumas coisas a considerar durante as chamadas de importação:

Eu tenho a seguinte estrutura:

mod/
    __init__.py
    main.py
    a.py
    b.py
    c.py
    d.py

main.py:

import mod.a
import mod.b as b
from mod import c
import d

dis.dis mostra a diferença:

  1           0 LOAD_CONST               0 (-1)
              3 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (mod.a)
              9 STORE_NAME               1 (mod)

  2          12 LOAD_CONST               0 (-1)
             15 LOAD_CONST               1 (None)
             18 IMPORT_NAME              2 (b)
             21 STORE_NAME               2 (b)

  3          24 LOAD_CONST               0 (-1)
             27 LOAD_CONST               2 (('c',))
             30 IMPORT_NAME              1 (mod)
             33 IMPORT_FROM              3 (c)
             36 STORE_NAME               3 (c)
             39 POP_TOP

  4          40 LOAD_CONST               0 (-1)
             43 LOAD_CONST               1 (None)
             46 IMPORT_NAME              4 (mod.d)
             49 LOAD_ATTR                5 (d)
             52 STORE_NAME               5 (d)
             55 LOAD_CONST               1 (None)

No final, eles parecem iguais (STORE_NAME é o resultado em cada exemplo), mas vale a pena notar se você precisar considerar as quatro importações circulares a seguir:

Exemplo 1

foo/
   __init__.py
   a.py
   b.py
a.py:
import foo.b 
b.py:
import foo.a
>>> import foo.a
>>>

Isso funciona

exemplo2

bar/
   __init__.py
   a.py
   b.py
a.py:
import bar.b as b
b.py:
import bar.a as a
>>> import bar.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "bar\a.py", line 1, in <module>
    import bar.b as b
  File "bar\b.py", line 1, in <module>
    import bar.a as a
AttributeError: 'module' object has no attribute 'a'

Sem dados

exemplo3

baz/
   __init__.py
   a.py
   b.py
a.py:
from baz import b
b.py:
from baz import a
>>> import baz.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "baz\a.py", line 1, in <module>
    from baz import b
  File "baz\b.py", line 1, in <module>
    from baz import a
ImportError: cannot import name a

Problema semelhante ... mas claramente de x import y não é o mesmo que importar import xy como y

exemplo4

qux/
   __init__.py
   a.py
   b.py
a.py:
import b 
b.py:
import a
>>> import qux.a
>>>

Este também funciona

ahfx
fonte
0

Esta é a minha estrutura de diretórios do meu diretório atual:

.  
└─a  
   └─b  
     └─c
  1. A importdeclaração lembra todos os nomes intermediários .
    Esses nomes devem ser qualificados:

    In[1]: import a.b.c
    
    In[2]: a
    Out[2]: <module 'a' (namespace)>
    
    In[3]: a.b
    Out[3]: <module 'a.b' (namespace)>
    
    In[4]: a.b.c
    Out[4]: <module 'a.b.c' (namespace)>
  2. A from ... import ...declaração lembra apenas o nome importado .
    Este nome não deve ser qualificado:

    In[1]: from a.b import c
    
    In[2]: a
    NameError: name 'a' is not defined
    
    In[2]: a.b
    NameError: name 'a' is not defined
    
    In[3]: a.b.c
    NameError: name 'a' is not defined
    
    In[4]: c
    Out[4]: <module 'a.b.c' (namespace)>

  • Nota: Obviamente, reiniciei meu console Python entre as etapas 1 e 2.
MarianD
fonte
0

Como Jan Wrobel menciona, um aspecto das diferentes importações é o modo como as importações são divulgadas.

Módulo mymath

from math import gcd
...

Uso de mymath :

import mymath
mymath.gcd(30, 42)  # will work though maybe not expected

Se eu importar gcdapenas para uso interno, não divulgá-lo aos usuários demymath , isso pode ser inconveniente. Eu tenho isso com bastante frequência e, na maioria dos casos, quero "manter meus módulos limpos".

Além da proposta de Jan Wrobel de ocultar um pouco mais isso import math, comecei a ocultar as importações da divulgação usando um sublinhado principal:

# for instance...
from math import gcd as _gcd
# or...
import math as _math

Em projetos maiores, essa "melhor prática" permite controlar exatamente o que é divulgado para importações subsequentes e o que não é. Isso mantém meus módulos limpos e compensa em um determinado tamanho de projeto.

Matheburg
fonte