Tudo bem ter várias classes no mesmo arquivo em Python?
Sim. Tanto de uma perspectiva filosófica quanto prática.
No Python, os módulos são um espaço para nome que existe uma vez na memória.
Digamos que tivemos a seguinte estrutura de diretórios hipotéticos, com uma classe definida por arquivo:
Defines
abc/
|-- callable.py Callable
|-- container.py Container
|-- hashable.py Hashable
|-- iterable.py Iterable
|-- iterator.py Iterator
|-- sized.py Sized
... 19 more
Todas essas classes estão disponíveis no collections
módulo e (existem, de fato, 25 no total) definidas no módulo de biblioteca padrão em_collections_abc.py
Existem algumas questões aqui que, acredito, tornam a _collections_abc.py
superior à estrutura de diretório hipotético alternativa.
- Esses arquivos são classificados em ordem alfabética. Você pode classificá-los de outras maneiras, mas não conheço um recurso que classifique arquivos por dependências semânticas. A origem do módulo _collections_abc é organizada por dependência.
- Em casos não patológicos, os módulos e as definições de classe são singletons, ocorrendo uma vez na memória. Haveria um mapeamento bijetivo de módulos para classes - tornando os módulos redundantes.
- O crescente número de arquivos torna menos conveniente a leitura casual das classes (a menos que você tenha um IDE que simplifique) - tornando-o menos acessível para pessoas sem ferramentas.
Você é impedido de dividir grupos de classes em módulos diferentes quando achar desejável do ponto de vista organizacional e do namespace?
Não.
Do Zen de Python , que reflete a filosofia e os princípios sob os quais ele cresceu e evoluiu:
Os espaços para nome são uma ótima idéia - vamos fazer mais!
Mas tenhamos em mente que também diz:
Flat é melhor que aninhado.
Python é incrivelmente limpo e fácil de ler. Incentiva você a lê-lo. Colocar cada classe separada em um arquivo separado desencoraja a leitura. Isso vai contra a filosofia central do Python. Veja a estrutura da Biblioteca Padrão , a grande maioria dos módulos são módulos de arquivo único, não pacotes. Eu diria a você que o código Python idiomático é escrito no mesmo estilo que a lib padrão do CPython.
Aqui está o código real do módulo da classe base abstrata . Eu gosto de usá-lo como referência para a denotação de vários tipos abstratos na linguagem.
Você diria que cada uma dessas classes deve exigir um arquivo separado?
class Hashable:
__metaclass__ = ABCMeta
@abstractmethod
def __hash__(self):
return 0
@classmethod
def __subclasshook__(cls, C):
if cls is Hashable:
try:
for B in C.__mro__:
if "__hash__" in B.__dict__:
if B.__dict__["__hash__"]:
return True
break
except AttributeError:
# Old-style class
if getattr(C, "__hash__", None):
return True
return NotImplemented
class Iterable:
__metaclass__ = ABCMeta
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
if _hasattr(C, "__iter__"):
return True
return NotImplemented
Iterable.register(str)
class Iterator(Iterable):
@abstractmethod
def next(self):
'Return the next item from the iterator. When exhausted, raise StopIteration'
raise StopIteration
def __iter__(self):
return self
@classmethod
def __subclasshook__(cls, C):
if cls is Iterator:
if _hasattr(C, "next") and _hasattr(C, "__iter__"):
return True
return NotImplemented
class Sized:
__metaclass__ = ABCMeta
@abstractmethod
def __len__(self):
return 0
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
if _hasattr(C, "__len__"):
return True
return NotImplemented
class Container:
__metaclass__ = ABCMeta
@abstractmethod
def __contains__(self, x):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
if _hasattr(C, "__contains__"):
return True
return NotImplemented
class Callable:
__metaclass__ = ABCMeta
@abstractmethod
def __call__(self, *args, **kwds):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Callable:
if _hasattr(C, "__call__"):
return True
return NotImplemented
Então, cada um deve ter seu próprio arquivo?
Espero que não.
Esses arquivos não são apenas códigos - são documentação sobre a semântica do Python.
São talvez 10 a 20 linhas em média. Por que eu deveria ter que ir para um arquivo completamente separado para ver outras 10 linhas de código? Isso seria altamente impraticável. Além disso, haveria importações de clichê quase idênticas em cada arquivo, adicionando mais linhas de código redundantes.
Eu acho bastante útil saber que existe um único módulo em que posso encontrar todas essas classes básicas abstratas, em vez de precisar examinar uma lista de módulos. Vê-los em contexto um com o outro me permite entendê-los melhor. Quando vejo que um iterador é um iterável, posso revisar rapidamente o que é um iterável olhando para cima.
Às vezes, acabo tendo algumas aulas muito curtas. Eles permanecem no arquivo, mesmo que precisem crescer com o tempo. Às vezes, módulos maduros têm mais de 1000 linhas de código. Mas o ctrl-f é fácil, e alguns IDE facilitam a visualização dos contornos do arquivo - portanto, independentemente do tamanho do arquivo, você pode acessar rapidamente qualquer objeto ou método que esteja procurando.
Conclusão
Minha direção, no contexto do Python, é preferir manter definições de classe relacionadas e semanticamente semelhantes no mesmo arquivo. Se o arquivo crescer tanto que se torne pesado, considere uma reorganização.
class SomeException extends \Exception {}