Frage Wie entwerfe ich eine Klasse in Python?


Ich hatte einige wirklich tolle Hilfe bei meinen vorherigen Fragen zum Erkennen von Pfoten und Zehen innerhalb einer Pfote, aber alle diese Lösungen funktionieren nur für jeweils eine Messung.

Jetzt habe ich Daten das besteht aus:

  • ungefähr 30 Hunde;
  • jeder hat 24 Messungen (unterteilt in mehrere Untergruppen);
  • jede Messung hat mindestens 4 Kontakte (einen für jede Pfote) und
    • Jeder Kontakt ist in 5 Teile und
    • hat verschiedene Parameter wie Kontaktzeit, Ort, Gesamtkraft usw.

alt text

Offensichtlich wird es nicht ausreichen, alles in ein großes Objekt zu kleben, also dachte ich, dass ich Klassen anstelle der aktuellen Funktionen verwenden müsste. Aber obwohl ich das Python-Kapitel über Klassen gelesen habe, kann ich es nicht auf meinen eigenen Code anwenden (GitHub Verbindung)

Ich habe auch das Gefühl, dass es ziemlich seltsam ist, alle Daten zu verarbeiten jeden Zeit, ich möchte einige Informationen bekommen. Sobald ich die Standorte jeder Pfote kenne, gibt es keinen Grund mehr, dies neu zu berechnen. Außerdem möchte ich alle Pfoten desselben Hundes vergleichen, um festzustellen, welcher Kontakt zu welcher Pfote gehört (vorne / hinten, links / rechts). Dies würde ein Chaos werden, wenn ich weiterhin nur Funktionen verwenden würde.

Jetzt suche ich einen Rat, wie ich Klassen erstellen kann, die mir erlauben, meine Daten zu verarbeiten (Link zu den gezippten Daten eines Hundes) auf eine vernünftige Art und Weise.


136
2017-11-17 09:43


Ursprung


Antworten:


Wie man eine Klasse entwirft.

  1. Schreib die Wörter auf. Du hast damit angefangen. Manche Leute nicht und wundern sich, warum sie Probleme haben.

  2. Erweitern Sie Ihre Wörter in einfache Aussagen darüber, was diese Objekte tun werden. Das heißt, notieren Sie die verschiedenen Berechnungen, die Sie auf diesen Dingen machen werden. Ihre kurze Liste von 30 Hunden, 24 Messungen, 4 Kontakten und mehreren "Parametern" pro Kontakt ist interessant, aber nur ein Teil der Geschichte. Ihre "Standorte jeder Pfote" und "vergleichen Sie alle Pfoten desselben Hundes, um zu bestimmen, welcher Kontakt zu welcher Pfote gehört" sind der nächste Schritt in der Objektgestaltung.

  3. Unterstreiche die Substantive. Ernst. Einige Leute debattieren den Wert von diesem, aber ich finde, dass es für die ersten OO-Entwickler hilft. Unterstreiche die Substantive.

  4. Überprüfen Sie die Substantive. Generische Substantive wie "Parameter" und "Messung" müssen durch spezifische, konkrete Substantive ersetzt werden, die für Ihr Problem in Ihrer Problemdomäne gelten. Besonderheiten helfen, das Problem zu klären. Generika elide Details.

  5. Schreiben Sie für jedes Substantiv ("Kontakt", "Pfote", "Hund" usw.) die Attribute dieses Substantivs und die Aktionen, an denen das Objekt beteiligt ist. Schlagen Sie nicht kurz. Jedes Attribut. "Data Set enthält 30 Hunde" zum Beispiel ist wichtig.

  6. Identifizieren Sie für jedes Attribut, ob es sich um eine Beziehung zu einem definierten Nomen oder um eine andere Art von "primitiven" oder "atomaren" Daten wie eine Zeichenfolge oder ein Float oder etwas, das nicht reduzierbar ist, handelt.

  7. Für jede Aktion oder Operation müssen Sie herausfinden, welches Substantiv die Verantwortung trägt und welche Substantive lediglich teilnehmen. Es ist eine Frage der "Veränderlichkeit". Einige Objekte werden aktualisiert, andere nicht. Veränderbare Objekte müssen die volle Verantwortung für ihre Mutationen tragen.

  8. An dieser Stelle können Sie beginnen, Substantive in Klassendefinitionen umzuwandeln. Einige kollektive Substantive sind Listen, Wörterbücher, Tupel, Sets oder Nameduples, und Sie müssen nicht viel arbeiten. Andere Klassen sind komplexer, entweder aufgrund von komplexen abgeleiteten Daten oder wegen einiger Aktualisierungen / Mutationen, die durchgeführt werden.

Vergessen Sie nicht, jede Klasse isoliert mit Unittest zu testen.

Außerdem gibt es kein Gesetz, das besagt, dass Klassen veränderbar sein müssen. In Ihrem Fall zum Beispiel haben Sie fast keine veränderbaren Daten. Was Sie haben, sind abgeleitete Daten, die von Transformationsfunktionen aus dem Quell-Dataset erstellt werden.


427
2017-11-17 11:21



Die folgenden Ratschläge (ähnlich dem Rat von @ S.Lott) stammen aus dem Buch, Anfang Python: Vom Anfänger bis zum Profi

  1. Schreiben Sie eine Beschreibung Ihres Problems auf (was sollte das Problem tun?). Unterstreiche alle Substantive, Verben und Adjektive.

  2. Geh durch die Substantive und suche nach möglichen Klassen.

  3. Gehe durch die Verben und suche nach möglichen Methoden.

  4. Gehe durch die Adjektive und suche nach möglichen Attributen

  5. Weisen Sie Ihren Klassen Methoden und Attribute zu

Um die Klasse zu verfeinern, empfiehlt das Buch auch, dass wir Folgendes tun können:

  1. Notieren Sie sich eine Reihe von Anwendungsfällen (Szenarien) - Szenarien, wie Ihr Programm verwendet werden kann. Versuchen Sie, alles funktional abzudecken.

  2. Denken Sie Schritt für Schritt durch jeden Anwendungsfall und stellen Sie sicher, dass alles, was wir brauchen, abgedeckt ist.


22
2017-11-22 18:09



Ich mag den TDD-Ansatz ... Beginnen Sie damit, Tests für das zu schreiben, was das Verhalten sein soll. Und schreiben Sie Code, der besteht. Machen Sie sich an dieser Stelle nicht zu viele Gedanken über das Design, sondern erhalten Sie nur eine Testsuite und Software, die bestanden hat. Machen Sie sich keine Sorgen, wenn Sie mit einer einzigen großen hässlichen Klasse mit komplexen Methoden enden.

Manchmal wird während dieses anfänglichen Prozesses ein Verhalten festgestellt, das schwer zu testen ist und nur zur Testbarkeit zerlegt werden muss. Dies kann ein Hinweis darauf sein, dass eine separate Klasse gerechtfertigt ist.

Dann der spaßige Teil ... Refactoring. Nachdem Sie eine funktionierende Software haben, können Sie die komplexen Teile sehen. Oft werden kleine Verhaltensbereiche sichtbar, die eine neue Klasse vorschlagen, aber wenn nicht, dann suchen Sie nach Möglichkeiten, den Code zu vereinfachen. Extrahieren Sie Serviceobjekte und Wertobjekte. Vereinfachen Sie Ihre Methoden.

Wenn du git richtig verwendest (du verwendest git, nicht wahr?), Kannst du beim Refactoring sehr schnell mit einer bestimmten Dekomposition experimentieren und sie dann abbrechen und zurückgehen, wenn es die Dinge nicht vereinfacht.

Wenn Sie zuerst einen getesteten Arbeitscode schreiben, sollten Sie einen intimen Einblick in die Problemdomäne erhalten, die Sie mit dem Design-First-Ansatz nicht so leicht erreichen konnten. Das Schreiben von Tests und Code bringt dich an die Lähmung "wo beginne ich".


13
2017-11-22 18:42



Die ganze Idee von OO-Design ist es, deine Code-Karte zu deinem Problem zu machen. Wenn du zum Beispiel den ersten Schritt eines Hundes willst, machst du so etwas wie:

dog.footstep(0)

Nun kann es sein, dass Sie in Ihrem Fall die Rohdatendatei einlesen und die Schrittpositionen berechnen müssen. All dies könnte in der Funktion step () versteckt sein, so dass es nur einmal passiert. Etwas wie:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[Dies ist jetzt eine Art Cache-Muster. Das erste Mal, wenn es geht und liest die Schrittdaten, nachfolgende Zeiten es gerade von self._footsteps.]

Aber ja, OO-Design richtig zu machen kann schwierig sein. Denken Sie mehr darüber nach, was Sie mit Ihren Daten tun möchten und welche Methoden Sie für welche Klassen anwenden müssen.


3
2017-11-17 10:07



Deine Substantive, Verben, Adjektive zu schreiben, ist ein großartiger Ansatz, aber ich denke eher, dass Klassendesign die Frage stellt Welche Daten sollten versteckt werden??

Stellen Sie sich vor, Sie hätten eine Query Objekt und a Database Objekt:

Das Query Objekt wird Ihnen helfen, eine Abfrage zu erstellen und zu speichern - speichern, ist der Schlüssel hier, als eine Funktion könnte Ihnen helfen, eine ebenso einfach zu erstellen. Vielleicht könntest du bleiben: Query().select('Country').from_table('User').where('Country == "Brazil"'). Es spielt keine Rolle, wie die Syntax aussieht - das ist Ihre Aufgabe! - Der Schlüssel ist, dass das Objekt dir hilft etwas versteckenIn diesem Fall die Daten, die zum Speichern und Ausgeben einer Abfrage erforderlich sind. Die Kraft des Objekts kommt von der Syntax, es zu benutzen (in diesem Fall eine clevere Verkettung) und nicht zu wissen, was es speichert, damit es funktioniert. Wenn richtig gemacht Query Objekt könnte Abfragen für mehr als eine Datenbank ausgeben. Es würde intern ein spezifisches Format speichern, könnte aber leicht in andere Formate konvertiert werden (Postgres, MySQL, MongoDB).

Jetzt lass uns durchdenken Database Objekt. Was versteckt und speichert es? Nun, es kann natürlich nicht den gesamten Inhalt der Datenbank speichern, da wir eine Datenbank haben! Also, was ist der Sinn? Das Ziel ist es verberge, wie die Datenbank funktioniert von Leuten, die das benutzen Database Objekt. Gute Klassen vereinfachen das Nachdenken bei der Manipulation des internen Zustands. Dafür Database Objekt Sie könnten verbergen, wie die Netzwerkaufrufe funktionieren, oder Batch-Abfragen oder Updates, oder eine Caching-Schicht bereitstellen.

Das Problem ist das Database Objekt ist riesig. Es stellt dar, wie man auf eine Datenbank zugreift, also unter der Decke alles und jeden tun kann. Netzwerk-, Caching- und Batching-Funktionen sind je nach System recht schwierig, daher wäre es sehr hilfreich, sie zu verstecken. Aber wie viele Leute bemerken werden, ist eine Datenbank wahnsinnig komplex und je weiter Sie von den unbearbeiteten DB-Aufrufen entfernt sind, desto schwieriger ist es, die Leistung zu optimieren und zu verstehen, wie die Dinge funktionieren.

Dies ist der grundlegende Kompromiss von OOP. Wenn Sie die richtige Abstraktion auswählen, wird das Codieren vereinfacht (String, Array, Dictionary). Wenn Sie eine zu große Abstraktion auswählen (Datenbank, EmailManager, NetworkingManager), kann es zu komplex werden, um wirklich zu verstehen, wie es funktioniert erwarten von. Das Ziel ist es Komplexität verstecken, aber einige Komplexität ist notwendig. Eine gute Faustregel ist, zu Beginn zu vermeiden Manager Objekte, und erstellen Sie stattdessen Klassen, die ähnlich sind structs - Alles, was sie tun, ist Daten zu halten, mit einigen Hilfsmethoden, um die Daten zu erstellen / manipulieren, um Ihr Leben einfacher zu machen. Zum Beispiel im Fall von EmailManager Beginnen Sie mit einer Funktion namens sendEmail das dauert ein EmailObjekt. Dies ist ein einfacher Ausgangspunkt und der Code ist sehr einfach zu verstehen.

Was Ihr Beispiel betrifft, denken Sie darüber nach, welche Daten zusammen sein müssen, um zu berechnen, wonach Sie suchen. Wenn Sie wissen wollten, wie weit ein Tier zum Beispiel ging, könnten Sie es haben AnimalStep und AnimalTrip (Sammlung von AnimalSteps) Klassen. Jetzt, da jede Reise alle Step-Daten enthält, sollte sie vielleicht darüber Bescheid wissen AnimalTrip.calculateDistance() macht Sinn.


2
2017-11-23 01:08



Nachdem Sie Ihren verknüpften Code abgeschöpft haben, scheint es mir besser zu sein nicht Entwerfen einer Hundeklasse an diesem Punkt. Eher sollten Sie verwenden Pandas und Datenrahmen. Ein Datenrahmen ist eine Tabelle mit Spalten. Ihr Datenrahmen würde Spalten haben wie: dog_id, contact_part, contact_time, contact_location, etc. Pandas verwendet Numpy Arrays hinter den Kulissen und es gibt viele bequeme Methoden für Sie:

  • Wählen Sie einen Hund z. : my_measurements['dog_id']=='Charly'
  • Sichere die Daten: my_measurements.save('filename.pickle')
  • Überlegen Sie zu verwenden pandas.read_csv() anstatt die Textdateien manuell zu lesen.

2
2017-11-24 00:08