Est-il correct d'avoir plusieurs classes dans le même fichier en Python?
Oui. Tant d'un point de vue philosophique que pratique.
En Python, les modules sont un espace de noms qui existe une fois en mémoire.
Supposons que nous ayons la structure de répertoires hypothétique suivante, avec une classe définie par fichier:
Defines
abc/
|-- callable.py Callable
|-- container.py Container
|-- hashable.py Hashable
|-- iterable.py Iterable
|-- iterator.py Iterator
|-- sized.py Sized
... 19 more
Toutes ces classes sont disponibles dans le collections
module et (il y en a en fait 25 au total) définies dans le module de bibliothèque standard dans_collections_abc.py
Il y a quelques problèmes ici que je crois le fait _collections_abc.py
supérieur à la structure de répertoire hypothétique alternative.
- Ces fichiers sont classés par ordre alphabétique. Vous pouvez les trier d'autres manières, mais je ne connais pas de fonctionnalité qui trie les fichiers par dépendances sémantiques. La source du module _collections_abc est organisée par dépendance.
- Dans les cas non pathologiques, les modules et les définitions de classe sont des singletons, se produisant une fois chacun en mémoire. Il y aurait une bijection de modules sur les classes - de le licencier modules.
- Le nombre croissant de fichiers rend la lecture des classes plus simple (à moins que vous n'ayez un IDE simple), ce qui le rend moins accessible aux personnes sans outils.
Vous empêche de vous casser les groupes de classes en différents modules lorsque vous paraît opportun d'un espace de nommage et plan organisationnel?
Non.
Depuis le zen de Python , ce qui reflète la philosophie et les principes sous lequel elle a poussé et se sont transformés:
Espaces de noms sont un klaxonnent grande idée - faisons plus de ceux-là!
Mais gardons à l'esprit qu'il dit aussi:
L'appartement est meilleur que l'emboîtement.
Python est incroyablement propre et facile à lire. Il vous encourage à le lire. Mettant toutes les classes séparées dans un fichier séparé lecture de dissuade. Cela va à l'encontre de la philosophie de base de Python. Considérer la structure de la bibliothèque standard , la grande majorité des modules sont des modules seul fichier, pas de paquets. Je vous soumets que le code Python idiomatique est écrit dans le même style que la lib standard CPython.
Voici le code réel du module de classe de base abstraite . Je tiens à servir de référence pour la dénotation de divers types abstraits dans le langage.
Diriez-vous que chacune de ces classes doivent exiger un fichier séparé?
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
Alors, devraient-ils chacun avoir leur propre dossier?
J'espère que non.
Ces fichiers ne sont pas seulement le code - ce sont des documents sur la sémantique de Python.
Ils sont peut-être 10 à 20 lignes en moyenne. Pourquoi devrais-je aller dans un fichier à part entière de voir un autre 10 lignes de code? Ce serait très peu pratique. De plus, il y aurait des importations de passe-partout presque identiques sur chaque fichier, ajoutant des lignes de code redondantes.
Je trouve assez utile de savoir qu'il existe un seul module où je peux trouver toutes ces classes de base abstraites, au lieu d'avoir à parcourir une liste de modules. Les visualiser en contexte les uns avec les autres me permet de mieux les comprendre. Quand je vois qu'un Iterator est un Iterable, je peux rapidement revoir ce qu'est un Iterable en jetant un coup d'œil.
Je me retrouve parfois par avoir un couple de classes très courts. Ils restent dans le dossier, même s'ils doivent s'agrandir avec le temps. Parfois, mûrir modules ont plus de 1000 lignes de code. Mais Ctrl-f est facile, et certains de IDE, il est facile voir les contours du dossier - alors peu importe la taille du fichier, vous pouvez aussi vous rendre à tout objet ou méthode que vous cherchez.
Conclusion
Ma direction, dans le contexte de Python, est de préférer conserver les définitions de classe apparentées et sémantiquement similaires dans le même fichier. Si le fichier devient si volumineux qu'il devient trop lourd, envisagez une réorganisation.
class SomeException extends \Exception {}