J'arrive tout juste dans le monde Python après des années de Java et PHP. Bien que le langage lui-même soit assez simple, je me bats avec certains " des problèmes mineurs que je ne peux pas comprendre - et auxquels je n'ai pas trouvé de réponses dans les nombreux documents et tutoriels que j'ai lus jusqu'ici.
Pour le praticien expérimenté Python, cette question peut sembler idiote, mais je veux vraiment une réponse pour que je puisse aller plus loin avec le langage:
Dans Java et PHP ( bien que pas strictement requis ), vous devez écrire chaque class
sur son propre fichier, avec le nom du fichier est celui de class
comme meilleure pratique.
Mais en Python, ou du moins dans les tutoriels que j'ai vérifiés, il est ok d'avoir plusieurs classes dans le même fichier.
Cette règle s'applique-t-elle à la production, au code prêt à être déployé ou est-elle effectuée uniquement par souci de concision dans le code éducatif uniquement?
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 module collections
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 qui, je pense, rendent le _collections_abc.py
supérieur à la structure de répertoires hypothétique alternative.
Non.
Du Zen of Python , qui reflète la philosophie et les principes sous lesquels il a grandi et évolué:
Les espaces de noms sont une excellente idée de klaxon - faisons-en plus!
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. Placer chaque classe distincte dans un fichier séparé décourage la lecture. Cela va à l'encontre de la philosophie de base de Python. Regardez la structure de la bibliothèque standard , la grande majorité des modules sont des modules à fichier unique, pas des packages. Je vous soumets que le code idiomatique Python est écrit dans le même style que la bibliothèque standard CPython.
Voici le code réel du module de classe de base abstrait . J'aime l'utiliser comme référence pour la dénotation de divers types abstraits dans la langue.
Diriez-vous que chacune de ces classes devrait nécessiter un fichier distinct?
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
Devraient-ils donc chacun avoir leur propre dossier?
J'espère que non.
Ces fichiers ne sont pas seulement du code - ils sont de la documentation sur la sémantique de Python.
Ce sont peut-être 10 à 20 lignes en moyenne. Pourquoi devrais-je avoir à aller dans un fichier complètement séparé pour voir 10 autres lignes de code? Ce serait très impraticable. 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 en quoi consiste un Iterable en levant les yeux.
Je finis parfois par avoir quelques cours très courts. Ils restent dans le dossier, même s'ils doivent s'agrandir avec le temps. Parfois, les modules matures ont plus de 1000 lignes de code. Mais ctrl-f est facile, et certains IDE facilitent la visualisation des contours du fichier - donc quelle que soit la taille du fichier, vous pouvez rapidement accéder à l'objet ou à la méthode que vous recherchez.
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.
Lors de la structuration de votre application en Python, vous devez penser en termes de packages et de modules.
Les modules concernent les fichiers dont vous parlez. C'est bien d'avoir un tas de classes dans le même module. L'objectif est que toutes les classes d'un même module servent le même objectif/la même logique. Si le module dure trop longtemps, pensez à le subdiviser en repensant votre logique.
N'oubliez pas de lire de temps en temps sur Index of Python Enhancement Proposals .
La vraie réponse à cela est générale et ne dépend pas du langage utilisé: ce qui doit être dans un fichier ne dépend pas principalement du nombre de classes qu'il définit. Cela dépend de la connectivité logique et de la complexité. Période.
Ainsi, si vous avez quelques très petites classes très interconnectées, elles doivent être regroupées dans le même fichier. Vous devez diviser une classe si elle n'est pas étroitement connectée à une autre classe ou si elle est trop complexe pour être incluse dans une autre classe.
Cela dit, la règle d'une classe par fichier est généralement une bonne heuristique. Cependant, il existe des exceptions importantes: une petite classe d'assistance qui n'est en fait que le détail d'implémentation de sa seule classe d'utilisateurs doit généralement être intégrée dans le fichier de cette classe d'utilisateurs. De même, si vous avez trois classes vector2
, vector3
, et vector4
, il y a peu de raisons de les implémenter dans des fichiers séparés.