Va bene avere più classi nello stesso file in Python?
Sì. Sia da una prospettiva filosofica che pratica.
In Python, i moduli sono uno spazio dei nomi che esiste una volta in memoria.
Supponiamo che avessimo la seguente ipotetica struttura di directory, con una classe definita per file:
Defines
abc/
|-- callable.py Callable
|-- container.py Container
|-- hashable.py Hashable
|-- iterable.py Iterable
|-- iterator.py Iterator
|-- sized.py Sized
... 19 more
Tutte queste classi sono disponibili nel collections
modulo e (ce ne sono, in effetti, 25 in totale) definite nel modulo libreria standard in_collections_abc.py
Ci sono un paio di problemi qui che credo siano _collections_abc.py
superiori all'ipotetica struttura di directory alternativa.
- Questi file sono ordinati alfabeticamente. Potresti ordinarli in altri modi, ma non sono a conoscenza di una funzione che ordina i file in base alle dipendenze semantiche. L'origine del modulo _collections_abc è organizzata per dipendenza.
- In casi non patologici, sia i moduli che le definizioni di classe sono singoli, che si verificano una volta ciascuno in memoria. Ci sarebbe una mappatura biiettiva di moduli su classi - rendendo i moduli ridondanti.
- Il numero crescente di file rende meno conveniente la lettura casuale delle classi (a meno che tu non abbia un IDE che lo renda semplice), rendendolo meno accessibile alle persone senza strumenti.
Ti viene impedito di suddividere gruppi di classi in moduli diversi quando lo ritieni opportuno dal punto di vista spaziale e organizzativo?
No.
Dallo Zen di Python , che riflette la filosofia e i principi in base ai quali è cresciuto e si è evoluto:
Gli spazi dei nomi sono una grande idea che suona il clacson: facciamo di più!
Ma ricordiamo che dice anche:
Flat è meglio di nidificato.
Python è incredibilmente pulito e di facile lettura. Ti incoraggia a leggerlo. Mettere ogni classe separata in un file separato scoraggia la lettura. Questo va contro la filosofia fondamentale di Python. Guarda la struttura della libreria standard , la stragrande maggioranza dei moduli sono moduli a file singolo, non pacchetti. Sottolineerei che il codice Python idiomatico è scritto nello stesso stile della libreria standard CPython.
Ecco il codice effettivo dal modulo di classe base astratto . Mi piace usarlo come riferimento per la denotazione di vari tipi astratti nella lingua.
Diresti che ognuna di queste classi dovrebbe richiedere un file separato?
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
Quindi dovrebbero avere ognuno il proprio file?
Spero di no.
Questi file non sono solo codice: sono documentazione sulla semantica di Python.
Sono forse in media da 10 a 20 righe. Perché dovrei andare in un file completamente separato per vedere altre 10 righe di codice? Sarebbe altamente poco pratico. Inoltre, ci sarebbero importazioni quasi identiche di plateplate su ogni file, aggiungendo righe di codice più ridondanti.
Trovo abbastanza utile sapere che esiste un singolo modulo in cui posso trovare tutte queste classi di base astratte, invece di dover consultare un elenco di moduli. Visualizzarli nel contesto reciproco mi permette di capirli meglio. Quando vedo che un Iterator è un Iterable, posso rapidamente rivedere in cosa consiste un Iterable alzando lo sguardo.
A volte finisco per avere un paio di lezioni molto brevi. Rimangono nel file, anche se devono crescere nel tempo. A volte i moduli maturi hanno oltre 1000 righe di codice. Ma ctrl-f è facile e alcuni IDE rendono semplice la visualizzazione dei contorni del file, quindi, indipendentemente dalla dimensione del file, puoi andare rapidamente a qualsiasi oggetto o metodo che stai cercando.
Conclusione
La mia direzione, nel contesto di Python, è quella di preferire mantenere definizioni di classe correlate e semanticamente simili nello stesso file. Se il file diventa così grande da diventare ingombrante, prendere in considerazione una riorganizzazione.
class SomeException extends \Exception {}