Ist es in Python in Ordnung, mehrere Klassen in derselben Datei zu haben?

18

Ich komme gerade nach Jahren mit Java und PHP in die Python-Welt. Obwohl die Sprache selbst ziemlich einfach ist, habe ich es mit einigen „kleinen“ Problemen zu tun, die ich nicht in den Griff bekommen kann - und auf die ich in den zahlreichen Dokumenten und Tutorials, die ich bisher gelesen habe, keine Antworten gefunden habe .

Für den erfahrenen Python-Praktiker mag diese Frage albern erscheinen, aber ich möchte wirklich eine Antwort darauf, damit ich mit der Sprache weitermachen kann:

In Java und PHP ( obwohl dies nicht unbedingt erforderlich ist ) wird von Ihnen erwartet, dass Sie jede classDatei in eine eigene Datei schreiben , wobei der Dateiname der classbeste ist.

Aber in Python oder zumindest in den von mir überprüften Tutorials ist es in Ordnung , mehrere Klassen in derselben Datei zu haben.

Gilt diese Regel für produktions- und einsatzbereiten Code oder wird sie nur aus Gründen der Kürze in Code nur für Bildungszwecke angewendet?

Olivier Malki
quelle

Antworten:

13

Ist es in Python in Ordnung, mehrere Klassen in derselben Datei zu haben?

Ja. Sowohl aus philosophischer als auch aus praktischer Sicht.

In Python sind Module ein Namespace, der einmal im Speicher vorhanden ist.

Angenommen, wir hatten die folgende hypothetische Verzeichnisstruktur mit einer definierten Klasse pro Datei:

                    Defines
 abc/
 |-- callable.py    Callable
 |-- container.py   Container
 |-- hashable.py    Hashable
 |-- iterable.py    Iterable
 |-- iterator.py    Iterator
 |-- sized.py       Sized
 ... 19 more

Alle diese Klassen sind im collectionsModul verfügbar und (insgesamt sind es 25) im Standardbibliotheksmodul in definiert_collections_abc.py

Hier gibt es einige Punkte, die meiner Meinung _collections_abc.pynach der alternativen hypothetischen Verzeichnisstruktur überlegen sind.

  • Diese Dateien sind alphabetisch sortiert. Sie könnten sie auf andere Weise sortieren, mir ist jedoch keine Funktion bekannt, mit der Dateien nach semantischen Abhängigkeiten sortiert werden. Die _collections_abc-Modulquelle ist nach Abhängigkeiten organisiert.
  • In nicht pathologischen Fällen sind sowohl Module als auch Klassendefinitionen Singletons, die jeweils einmal im Speicher vorkommen. Es würde eine bijektive Zuordnung von Modulen zu Klassen geben, wodurch die Module überflüssig würden.
  • Die zunehmende Anzahl von Dateien macht es weniger bequem, die Klassen beiläufig durchzulesen (es sei denn, Sie haben eine IDE, die es einfach macht) - dies macht es für Leute ohne Werkzeuge weniger zugänglich.

Werden Sie daran gehindert, Klassengruppen in verschiedene Module aufzuteilen, wenn Sie dies aus Sicht des Namensraums und der Organisation für wünschenswert halten?

Nein.

Aus dem Zen von Python , das die Philosophie und Prinzipien widerspiegelt, unter denen es gewachsen ist und sich entwickelt hat:

Namespaces sind eine großartige Idee - machen wir mehr davon!

Aber denken wir daran, dass es auch heißt:

Flach ist besser als verschachtelt.

Python ist unglaublich sauber und leicht zu lesen. Es ermutigt Sie, es zu lesen. Das Einfügen jeder Klasse in eine separate Datei rät vom Lesen ab. Dies widerspricht der Kernphilosophie von Python. Betrachten Sie die Struktur der Standardbibliothek , die überwiegende Mehrheit der Module besteht aus einzelnen Dateimodulen und nicht aus Paketen. Ich möchte Ihnen mitteilen, dass der idiomatische Python-Code im gleichen Stil wie der CPython-Standard lib geschrieben ist.

Hier ist der eigentliche Code aus dem abstrakten Basisklassenmodul . Ich verwende es gerne als Referenz für die Bezeichnung verschiedener abstrakter Typen in der Sprache.

Würden Sie sagen, dass für jede dieser Klassen eine separate Datei erforderlich ist?

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

Sollten sie also jeweils eine eigene Datei haben?

Ich hoffe nicht.

Diese Dateien sind nicht nur Code, sondern eine Dokumentation zur Semantik von Python.

Im Durchschnitt sind es vielleicht 10 bis 20 Zeilen. Warum sollte ich in eine völlig separate Datei gehen müssen, um weitere 10 Codezeilen zu sehen? Das wäre höchst unpraktisch. Darüber hinaus würde jede Datei nahezu identische Boilerplate-Importe enthalten, wodurch mehr redundante Codezeilen hinzugefügt würden.

Ich finde es sehr nützlich zu wissen, dass es ein einziges Modul gibt, in dem ich alle diese abstrakten Basisklassen finden kann, anstatt eine Liste von Modulen durchsehen zu müssen. Wenn ich sie im Zusammenhang miteinander betrachte, kann ich sie besser verstehen. Wenn ich sehe, dass ein Iterator ein Iterable ist, kann ich schnell überprüfen, woraus ein Iterable besteht, indem ich nach oben schaue.

Manchmal habe ich ein paar sehr kurze Stunden. Sie bleiben in der Datei, auch wenn sie mit der Zeit größer werden müssen. Manchmal haben ausgereifte Module über 1000 Codezeilen. Ctrl-F ist jedoch einfach, und einige IDEs vereinfachen das Anzeigen von Umrissen der Datei. Unabhängig davon, wie groß die Datei ist, können Sie schnell zu dem gewünschten Objekt oder der gewünschten Methode wechseln.

Fazit

Im Kontext von Python möchte ich, dass verwandte und semantisch ähnliche Klassendefinitionen in derselben Datei bleiben. Wenn die Datei so groß wird, dass sie unhandlich wird, ziehen Sie eine Reorganisation in Betracht.

Aaron Hall
quelle
1
Nun, obwohl ich verstehe, kann ich dank des von Ihnen eingereichten Codes, dass es in Ordnung ist, mehrere Klassen in derselben Datei zu haben, das Argument nicht sehr überzeugend finden. Zum Beispiel war es in PHP sehr verbreitet, eine ganze Datei mit nur folgendem Code zu haben:class SomeException extends \Exception {}
Olivier Malki,
3
Unterschiedliche Communities haben unterschiedliche Codierungsstandards. Java-Leute schauen sich Python an und sagen: "Warum sind mehrere Klassen pro Datei zulässig?". Python-Leute schauen sich Java an und sagen: "Warum benötigt jede Klasse eine eigene Datei?". Es ist am besten, dem Stil der Community zu folgen, in der Sie arbeiten.
Gort the Robot
Ich bin auch hier verwirrt. Anscheinend habe ich mit meiner Antwort einige Dinge über Python falsch verstanden. Aber würde man "flach ist besser als verschachtelt" anwenden, um einer Klasse so viele Methoden wie möglich hinzuzufügen? Im Allgemeinen würde ich denken, dass die Prinzipien von Kohäsion und SRP immer noch für ein Modul gelten, das Module bevorzugt, die Klassen bereitstellen, die in ihrer Funktionalität eng miteinander verbunden sind (wenn auch sehr wohl mehr als eine Klasse, da ein Modul ein gröberes Paketkonzept als eine einzelne Klasse modelliert ), zumal alle Modul-Scope-Variablen (die hoffentlich im Allgemeinen vermieden werden) an Umfang gewinnen würden.
1
Das Zen von Python ist eine Liste von Prinzipien, die in Spannung miteinander stehen. Man könnte in Übereinstimmung mit Ihrem Punkt mit "Sparse ist besser als dicht." - was sofort folgt: "Flat ist besser als verschachtelt." Einzelne Zeilen aus The Zen of Python können leicht missbraucht und auf die Spitze getrieben werden. Als Ganzes kann dies jedoch die Kunst unterstützen, Gemeinsamkeiten zu finden, bei denen vernünftige Menschen möglicherweise anderer Meinung sind. Ich glaube nicht, dass die Leute meine Codebeispiele für dicht halten würden, aber was Sie beschreiben, klingt für mich sehr dicht.
Aaron Hall
Vielen Dank, dass Sie Jeffrey Albertson / Comic Guy. :) Die meisten Benutzer von Python sollten nicht die (doppelten Unterstriche) Spezialmethoden verwenden, aber sie ermöglichen es einem Kerndesigner / -architekten, sich an der Metaprogrammierung zu beteiligen, um Operatoren, Komparatoren, Indexnotationen, Kontexte und andere benutzerdefinierte Funktionen zu nutzen Sprachmerkmale. Solange sie nicht gegen das Prinzip der geringsten Überraschung verstoßen, halte ich das Verhältnis von Schaden zu Wert für infinitesimal.
Aaron Hall
4

Wenn Sie Ihre Anwendung in Python strukturieren, müssen Sie in Paketen und Modulen denken.

Bei Modulen handelt es sich um die Dateien, über die Sie sprechen. Es ist in Ordnung, mehrere Klassen im selben Modul zu haben. Das Ziel ist, dass alle Klassen innerhalb desselben Moduls denselben Zweck / dieselbe Logik erfüllen sollen. Wenn das Modul zu lang wird, sollten Sie es durch eine Neugestaltung Ihrer Logik unterteilen.

Vergessen Sie nicht, von Zeit zu Zeit den Index der Python-Verbesserungsvorschläge zu lesen .


quelle
2

Die eigentliche Antwort darauf ist allgemein und hängt nicht von der verwendeten Sprache ab: Was in einer Datei enthalten sein sollte, hängt nicht in erster Linie davon ab, wie viele Klassen definiert werden. Es hängt von der logischen Vernetzung und von der Komplexität ab. Zeitraum.

Wenn Sie also einige sehr kleine Klassen haben, die stark miteinander verbunden sind, sollten sie in derselben Datei gebündelt werden. Sie sollten eine Klasse aufteilen, wenn sie entweder nicht fest mit einer anderen Klasse verbunden ist oder zu komplex ist, um in eine andere Klasse aufgenommen zu werden.

Die Regel für eine Klasse pro Datei ist jedoch normalerweise eine gute Heuristik. Es gibt jedoch wichtige Ausnahmen: Eine kleine Hilfsklasse, die eigentlich nur das Implementierungsdetail ihrer einzigen Benutzerklasse ist, sollte im Allgemeinen in der Datei dieser Benutzerklasse gebündelt werden. Wenn Sie drei Klassen vector2haben vector3, und vector4, gibt es wahrscheinlich wenig Grund, sie in separaten Dateien zu implementieren.

In cmaster stellst du Monica wieder her
quelle