Est-il correct d'avoir plusieurs classes dans le même fichier en Python?

18

J'arrive tout juste dans le monde Python après des années de Java et de PHP. Bien que la langue elle-même soit à peu près simple, je me bats avec 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é de Python, cette question peut sembler idiote, mais je veux vraiment une réponse pour que je puisse aller plus loin avec le langage:

En Java et PHP ( bien que ce ne soit pas strictement requis ), vous devez écrire chacun classsur son propre fichier, le nom du fichier étant celui de la classmeilleure pratique.

Mais en Python, ou du moins dans les tutoriels que j'ai vérifiés, il est correct 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?

Olivier Malki
la source

Réponses:

13

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 collectionsmodule 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.pysupé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.

Aaron Hall
la source
1
Eh bien, bien que je comprenne, grâce au code que vous avez soumis qu'il est OK d'avoir plusieurs classes dans le même fichier, je ne trouve pas l'argument très convaincant. Par exemple, il était très courant en PHP d'avoir un fichier entier avec seulement du code similaire à ceci:class SomeException extends \Exception {}
Olivier Malki
3
Les différentes communautés ont des normes de codage. Les gens Java regardent python et disent "pourquoi autorise-t-il plusieurs classes par fichier!?". Les gens de Python regardent Java et disent "pourquoi faut-il que chaque classe ait son propre fichier!?". Il est préférable de suivre le style de la communauté dans laquelle vous travaillez.
Gort the Robot
Je ne comprends pas, là aussi. Il semble avoir mal compris certaines choses au sujet de Python avec ma réponse. Mais peut -on appliquer « à plat est meilleur que imbriqué » pour ajouter autant de méthodes que possible à une classe? En général , je pense que les principes de cohésion et de SRP appliquent encore un module en faveur des modules qui dispensent des cours qui sont étroitement liées les unes aux autres en fonction (mais peut - être bien plus d'une classe depuis un des modèles du module un concept d'emballage de plus gros qu'une seule classe ), d' autant plus que toutes les variables module de portée (mais espérons éviter en général) augmenteraient de portée.
1
Le Zen de Python est une liste de principes qui sont en tension entre eux. On pourrait répondre en accord avec votre point de vue, "clairsemé est mieux que dense." - qui suit immédiatement, « plat est meilleur que imbriquée. » Lignes individuelles du zen de Python peuvent facilement être mal utilisés et poussés à l' extrême, mais pris dans son ensemble, il peut aider à l'art de coder et de trouver un terrain d' entente où les gens raisonnables peuvent être en désaccord autrement. Je ne pense pas que les gens considéreraient mes exemples de code denses, mais ce que vous décrivez me semble très dense.
Aaron Hall
Merci Jeffrey Albertson / Vendeur de BD. :) La plupart des utilisateurs de Python ne devraient pas utiliser les méthodes spéciales (double soulignement), mais elles permettent à un concepteur / architecte principal de s'engager dans la méta-programmation pour faire une utilisation personnalisée des opérateurs, des comparateurs, de la notation en indice, des contextes et autres caractéristiques linguistiques. Dans la mesure où ils ne Je pense violent pas le principe de moindre surprise, le préjudice causé à taux de valeur est infime.
Aaron Hall
4

Dans l'organisation de votre application en Python, il faut penser en termes de composants 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 dans le même module doivent servir le même but / logique. Si le module dure trop longtemps, pensez à le subdiviser en repensant votre logique.

Ne pas oublier de lire de temps en temps sur l' indice des propositions Python mise en valeur .


la source
2

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 réalité 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, vector3et vector4, il est probable guère de raison de les appliquer dans des fichiers séparés.

cmaster - réintégrer monica
la source