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 collections
Modul verfügbar und (insgesamt sind es 25) im Standardbibliotheksmodul in definiert_collections_abc.py
Hier gibt es einige Punkte, die meiner Meinung _collections_abc.py
nach 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.
class SomeException extends \Exception {}
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
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
vector2
habenvector3
, undvector4
, gibt es wahrscheinlich wenig Grund, sie in separaten Dateien zu implementieren.quelle