@techma: Gibt es in der Datei neben der Teilzeichenkette 'MODE NUMBER' keine anderen Merkmale an denen man die Zeilen in Abschnitte zerlegen kann? Insbesondere das Ende eines Blocks als „zwei Zeilen vor 'MODE NUMBER'“ ist es, was es schwierig(er) macht die Datei nicht komplett einlesen zu müssen. Obwohl das auch möglich sein sollte.
Die Zeilenlänge sollte 80 Zeichen nicht überschreiten, weil der Quelltext sonst in diversen Anzeigen, zum Beispiel in Terminals, oder auch hier im Forum an Stellen umgebrochen wird, die das ganze schwieriger zu lesen machen. Wenn man die konventionellen vier Leerzeichen pro Einrückebene verwendet und die Kommentare nicht hinter die Zeilen schreibt, sondern ihnen davor eigene Zeilen spendiert, dann ist das schon einfacher einzuhalten.
Eine weitere Möglichkeit sich eine komplette Ebene zu sparen, ist es den ganzen Quelltext der nichts mit dem Einlesen der Datei zu tun hat, *hinter* den ``with``-Block zu schreiben. Das ist alles ausser der ersten und der letzten Zeile von dem Block; wobei die letzte Zeile auch noch überflüssig ist, denn das ``with`` sorgt ja gerade dafür, dass die Datei auf jeden Fall beim verlassen des Blocks geschlossen wird.
Eine weitere Formatierungsfrage sind Leerzeichen um Operatoren und Zuweisungen ausserhalb von Parameter-/Argumentlisten. Sie erhöhen die Lesbarkeit.
Statt einzelne Zeilen zu kommentieren, könntest Du auch Gruppen von Zeilen mit Erklärungen versehen warum sie das machen, was sie machen. Dann muss man sich das Gesamtbild von (Teil)Algorithmen nicht mühsam zusammen puzzlen. Zum Beispiel wäre es auch schön eine Beschreibung des Formates zu haben, bevor man anfängt den Quelltext zu lesen, der die Daten verarbeitet.
Namen sollten ausserhalb von sehr begrenzten Ausdrücken, oder wenn es einfache Zähler sind, nicht nur einen Buchstaben lang sein. `l` hat zusätzlich, dass man am Namen nicht erkennt, wofür er eigentlich steht, noch das Problem, dass man ihn in vielen Schriftarten leicht mit einer 1 verwechseln kann. Man sollte Namen nicht abkürzen, solange es sich nicht allgemein bekannte Abkürzungen handelt, und der konkrete Typ sollte auch nicht im Namen enthalten sein. Ein Negativbeispiel ist hier `mlist`. Was ist `m` und muss das immer eine Liste sein!? Wenn man nämlich später fest stellt, dass eine andere Datenstruktur effizienter ist, oder man die einfache Liste durch einen selbst implementierten Container-Datentyp ersetzt, muss man überall den Namen anpassen, wenn man keinen sehr irreführenden Namen im Quelltext haben möchte.
Wenn man aus dem `l` ein `line` macht, bekommt man das Problem, dass man in der ersten Schleife die Namen `zeile` und `line` hat, die etwas unterschiedliches bedeuten. `zeile` müsste von der Bedeutung her eigentlich `zeilennummer` heissen. Mit `knoten`/`node` wiederholt sich dieses Muster noch einmal.
Man sollte sich bei der Namensgebung mühe geben. Wenn man einen Kommentar benötigt, der erklärt wofür der Name steht, dann ist das in der Regel ein Zeichen, dass der Name besser sein könnte. Denn eigentlich sollte *der* ja schon dem Leser die Bedeutung des Wertes vermitteln, der an den Namen gebunden ist.
Eine Deutsch/Englisch-Mischung finden die meisten Leute nicht so prickelnd. Zumal das zu solchen Problemen führen kann, wie weiter oben beschrieben, dass Werte mit verschiedenen Bedeutungen den „gleichen“ Namen bekommen, oder auch umgekehrt, dass Werte mit der selben Bedeutung innerhalb eines Programms mal den englischen und mal den deutschen Namen bekommen.
Der Test ob eine Zeichenkette in einer anderen Zeichenkette enthalten ist, geht mit dem ``in``-Operator einfacher und IMHO auch lesbarer als mit der `find()`-Methode.
Das manuelle mit zählen der Zeilennummer sollte man durch die `enumerate()`-Funktion ersetzen. Dann könnte man daraus auch eine „list comprehension” (LC) machen:
Code: Alles auswählen
mode_start_line_numbers = [
i + 5 for i, line in enumerate(lines) if 'MODE NUMBER' in line
]
Die Bestimmung von `knoten` verstehe ich nicht so ganz. Es sieht so aus, als wenn es sich dabei um die Anzahl der Knoten pro „Mode” handelt. Die liesse sich doch aber auch aus den vorliegenden Zeilenangaben einfacher berechnen, und vor allem ohne dass man sich darauf verlässt welche konkreten Werte die IDs in der ersten Spalte haben!? Das hier sollte den gleichen Wert ergeben:
Code: Alles auswählen
nodes_per_mode = mode_start_line_numbers[1] - mode_start_line_numbers[0] - 7
Weder das, noch Dein Code, ist übrigens robust gegen Dateien die weniger als zwei „Modes” enthalten.
Wenn die Node-Daten immer 5 Zeilen nach 'MODE NUMBER' anfangen und zwei Zeilen vor 'MODE NUMBER' enden, dann sollte es eigentlich auch genügen die erste, zweite und die letzte 'MODE NUMBER'-Zeile zu ermitteln. Oder die erste und die letzte und die Anzahl dieser Zeilen. Die Zeilenbereiche lassen sich daraus dann berechnen.
Was soll die Zahl im Namen von `nodedata1`? Namen durchnummerieren ist selten eine gute Idee. Der Name wird hier auch an ungünstigen Stellen an eine leere Liste gebunden. Das ist verwirrend und umständlich es einmal vor der Schleife und dann nach jedem „Gebrauch“ zu tun. So fragt man sich als Leser wo denn noch überall Werte in der Liste gesammelt werden ausser an der Stelle, wo Du die drei Koordinaten hinzufügst. Denn wenn da nicht noch andere Stellen sind, macht es keinen Sinn, dass der Code die Liste *nach* dem Gebrauch neu anlegt, und nicht *davor* und dann auch nur dort und nicht auch vor der Schleife. Letztlich ist der Name im vorliegenden Fall aber auch komplett überflüssig, denn man kann auch einfach eine anonyme, literale Liste mit den drei Werten hin schreiben.
Die Funktion enthält mit `dispData` und `nodelist` Namen, die zwar an Werte gebunden, dann aber nie wieder verwendet werden. Umgekehrt gibt es `nodeData` und `dispBib_adams` deren Herkunft ungeklärt ist. Werte die keine Konstanten sind, sollten Funktionen als Argumente betreten und nicht einfach so magisch existieren. Das macht Programme unübersichtlicher, schwerer zu verstehen, schlechter zu testen, und letztendlich fehleranfälliger wenn man etwas am Programm verändert.
Ungetestet und kommentarfrei:
Code: Alles auswählen
def adams_knotenverschiebungen_auslesen(
node_data, disp_bib_adams, dmp_filename
):
with open(dmp_filename) as dmp_file:
lines = list(dmp_file)
mode_start_line_numbers = [
i + 5 for i, line in enumerate(lines) if 'MODE NUMBER' in line
]
nodes_per_mode = mode_start_line_numbers[1] - mode_start_line_numbers[0] - 7
for mode_id, mode_lines in enumerate(
(lines[i:i + nodes_per_mode] for i in mode_start_line_numbers),
1
):
node_id2coordinates = dict()
for line in mode_lines:
node_id, x, y, z, _ = line.split(None, 4)
node_id = int(node_id)
if node_id in node_data:
node_id2coordinates[node_id] = map(float, [x, y, z])
disp_bib_adams[mode_id] = node_id2coordinates