Anregungen zur Objektorientierung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DocFisher
User
Beiträge: 50
Registriert: Donnerstag 29. November 2007, 21:04
Wohnort: Berlin
Kontaktdaten:

Hallo,

wie aus meinen vorherigen Beiträgen hervorgeht, versuch ich rein aus Interesse die Grundzüge des Programmierens zu lernen. Ein bisschen habe ich mich damit befasst, aber an den eigentlichen Kern, die Objektorientierung komme ich nicht richtig heran.

Für einen Bekannten habe ich ein Skript geschrieben, welches folgende Aufgabe lösen soll:
-----
Es existieren 2 Dateien, z.B. server.txt und db.txt. Diese enthalten jeweils eine Menge (ca. 4000) "Vorgang-ID's".
Als erstes sollen alle ID's gefunden werden, die nur in einer der beiden Dateien vorkommen. Ich nenne diese ID's "nohits".

In einem Verzeichnis liegen etliche Dateien, die diverse Informationen enthalten, darunter mindestens 1 Vorgangs-ID. Oft auch mehrere verschiedene ID's.
Dieses Verzeichnis soll nun durchsucht werden und alle Dateien, in denen mind. eine "nohit"-ID vorkommt sollen in eine Zip-Datei geschrieben werden.
-----

Mein Skript funktioniert, solange keine "ungewöhnlichen" Situationen auftreten. Sprich, ich habe keinerlei Exceptions etc. eingebaut. Das soll alles noch kommen. Was mir aus didaktischen Gründen viel wichtiger ist, ist die Objektorientierung. Deshalb wende ich mich mal wieder an Euch:

Habt ihr Anregungen, welche Klassen hier sinnvoll wären?


Ich möchte keine kompletten Lösungen, sondern nur Anregungen, die ich selbst umsetzen muss.
Zurzeit habe ich noch das Gefühl, dass hier Klassen etc. nicht sonderlich nützlich wären. Aber dies liegt vermutlich an meinem mangelnden Verständnis und der fehlenden Erfahrung...

Hier noch mein Skript:

Code: Alles auswählen

import re
import os
import zipfile
import time

## Verzeichnisse eingeben. Hier noch keine Pruefung auf Existenz
file1 = raw_input("Dateiname (1): ")
file2 = raw_input("Dateiname (2): ")
verzeichnis = raw_input("zu durchsuchendes Verzeichnis: ")

startzeit = time.time()
##Hier werden die Dateien zeilenweise in eine Liste eingelesen
t1 = (file(file1, 'r').readlines())
t2 = (file(file2, 'r').readlines())

##Mit der Mengenoperation Differenz der Daten ermitteln und als Liste speichern
nohits = list(frozenset(t1) - frozenset(t2))

##Etwas muehsames Konstrukt um die \n zu entfernen
nohit = []
for i in nohits:
   nohit.append(re.sub('\n', '', i))


##eine leere Zip Datei wird im Ordner Report angelegt
os.chdir(verzeichnis)
os.mkdir('Report')
os.chdir('Report')
storage = zipfile.ZipFile('Mismatch.zip', 'w')
os.chdir(verzeichnis)


for datei in os.listdir(verzeichnis):
    if os.path.isfile(datei):
        f = file(datei, 'r')
        inhalt = f.read()
        f.close()
        for i in nohit:
            if i in inhalt:
                storage.write(datei, os.path.basename(datei), zipfile.ZIP_DEFLATED)
storage.close()


zeit = int(time.time() - startzeit)

##Zusammenfassung:
print "---------------------------------------------"
print
print "%d Vorgangsnummern in %s" %(len(t1), file1)
print "%d Vorgangsnummern in %s" %(len(t2), file2)
print "%d unbearbeitete Vorgangsnummern" %len(nohits)
print "Die zugehoerigen Dateien wurden in der Datei"
print "Mismatch.zip im Unterordner Report gespeichert"
print
print "benoetigte Zeit", zeit, "Sekunden."
print "---------------------------------------------"
raw_input("")
Viele Grüße, Jens
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

DocFisher hat geschrieben:Habt ihr Anregungen, welche Klassen hier sinnvoll wären?
Keine. Alles in Funktionen verpacken und das reicht dann.

Man sollte nicht den Fehler nicht objektorientierte Probleme mit OOP zu lösen. Aber das habe ich im Thread Was ist OOP eigentlich? schon gesagt - lies ihn dir mal durch (mit den ganzen Kommentaren), das ist meiner Meinung nach eine recht gute Diskussion dazu.

Edit: Wozu das letzte ``raw_input()``? Das ist Quatsch und ungemein Störend in der Konsole, wenn es sich nicht einfach beendet sondern auf eine Extraaufforderung wartet. Die Klammern um t1 und t2 bringen auch genau gar nichts - lass sie weg.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
DocFisher
User
Beiträge: 50
Registriert: Donnerstag 29. November 2007, 21:04
Wohnort: Berlin
Kontaktdaten:

Hallo Leonidas,
Zurzeit habe ich noch das Gefühl, dass hier Klassen etc. nicht sonderlich nützlich wären. Aber dies liegt vermutlich an meinem mangelnden Verständnis und der fehlenden Erfahrung...
Dann lag ich ja doch richtig...

Das letzte raw_input war dazu gedacht, die Konsole unter Windows offen zu halten, damit man die Ausgabe noch lesen konnte. Aber das ganze ist eigentlich unnötig.

Die Klammern sind jetzt weg. Ich dachte, die bräuchte ich, um eine Liste zu erzeugen. Danke für den Hinweis!

Ich werde Deine Vorschläge mal umsetzten und mir auch den langen Thread anschauen. Ich hoffe, ich kann folgen ;-)

Gruß und Dank, Jens
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich möchte noch anmerken: objektorientiert != klassenbasiert. Objekte identifizieren und ihr Verhalten modellieren ist bei der Aufgabe, zwei Mengen von IDs zu vergleichen, kein Ansatz, der besser ist, als ein Ad-hoc-Design. Will sagen, dein Problem ist so einfach, da bringt dir ein objektorientierter Entwufs keinen Vorteil.

Wichtiger fände ich, die Aufgabe in Teilaufgaben zu zerlegen und dann jede Teilaufgabe in einer Prozedur (oder Funktion) zu abstrahieren - also eine einfache strukturierte Analyse nach dem state of the art der frühen 70er Jahre zu machen.

Somit würde ich dir die Evolution deines "Skript" in ein strukturiertes Programm vorschlagen, bevor du dich den 80ern mit OO oder den 90ern [TBD]DD als angesagte Entwurfsmethode näherst ;)

Eine gute Faustformel ist: Überall dort, wo du den Wunsch verspürt hast, einen Kommentar zu schreiben, sollte man darüber nachdenken, ob die folgenden Zeilen nicht in eine eigene Funktion gehören. Interessanter Weise hast du den längsten (und am kompliziertesten aussehenden) Block
ganz am Ende des Scripts dann nicht mehr kommentiert :)

PS: Objektorientiert in Python ist ein bisschen schwer, denn Python ist eher prozedural oder funktional als wirklich OO, was man gerade bei den Dateioperationen bemerkt. Schick wäre IMHO sowas:

Code: Alles auswählen

for item in folder:
  if item.isfile():
    content = item.read()
    for i in nohits:
      if i in content: storage.add(item)
Oder ohne spezielle Syntax für for, if und in nur mit Objekten und Nachrichten in Pseudo-Ruby:

Code: Alles auswählen

folder.each {|item|
  item.isfile.iftrue {
    content = item.read()
    nohits.any({|id| content.contains(id)}).ifTrue {
      storage.add(item) 
    }
  }
}
Diese Lösung würde ich objektorientiert nennen, sie ist aber nicht zwingend besser als die Python-Lösung mit mehr Syntax.

Stefan
Zuletzt geändert von sma am Freitag 28. Dezember 2007, 17:52, insgesamt 1-mal geändert.
DocFisher
User
Beiträge: 50
Registriert: Donnerstag 29. November 2007, 21:04
Wohnort: Berlin
Kontaktdaten:

Hallo sma,

mittlerweile habe ich eingesehen, dass hier OO eher mehr als minder Quatsch ist.
Ich versuche nur gerade auf Teufel-komm-raus irgendwie mit Obbjekten und Klassen zu arbeiten, da ich mich bald an wxpython wagen will....

Der Thread von Leonidas zum Thema "was ist OOP?" war sehr aufschlußreich.

Mittlerweile habe ich das Skript mit 4 Funktionen versehen:
eingabe()
get_nohits()
store_zip()
ausgabe()
In den nächsten Tagen werde ich versuchen, es noch ein bischen stabiler im Falle evt. Fehler zu machen, aber ansonsten erfüllt es seinen Zweck.

Thx anyway, Gruß, Jens
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

DocFisher hat geschrieben:Das letzte raw_input war dazu gedacht, die Konsole unter Windows offen zu halten, damit man die Ausgabe noch lesen konnte. Aber das ganze ist eigentlich unnötig.
Wenn sowas nötig ist (und ganz ehrlich, das ist es eigentlich nie, weil man das immer anders - besser Lösen kann), dann solltest du besser die "Saubere" Enter-on-quit Lösung verwenden. Denn wenn dein Programm crasht, dann hilft die das ``raw_input`` am Schluss nämlich auch überhaupt nicht.
DocFisher hat geschrieben:Die Klammern sind jetzt weg. Ich dachte, die bräuchte ich, um eine Liste zu erzeugen. Danke für den Hinweis!
Runde Klammern erstellen keine Listen sondern Tupel. Und im Gegensatz zu Listen wird ein Tupel nicht mit ``(item)`` sondern nur mit ``(item,)`` erstellt, also da muss noch ein Komma hin. Beachte folgenden Code:

Code: Alles auswählen

>>> type((1))
<type 'int'>
>>> type((1,))
<type 'tuple'>
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@sma: Ich finde Du hängst Dich da wieder zu sehr an Syntax auf. Python als Sprache ist nicht weniger OOP als Ruby. Was Dir hier aufstösst ist die Standardbibliothek, die mit "zu primitiven" Objekten hantiert. Kennst Du Jason Orendorff's path.py?
DocFisher
User
Beiträge: 50
Registriert: Donnerstag 29. November 2007, 21:04
Wohnort: Berlin
Kontaktdaten:

@leonidas
Woher nimmst Du diese Geduld nur?!?
Ich danke Dir für die vermutlich schon 1000fach geposteten Hinweise (und werde sie in Zukunft beherzigen)!
Und ich werde wohl doch nochmal die Kapitel über Basics bezüglich Listen, Tupels und Dicts durcharbeiten, bevor ich weitermache.

Die exit Methode ist fein!!!

Thx, Jens
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

BlackJack hat geschrieben:@sma: Ich finde Du hängst Dich da wieder zu sehr an Syntax auf. Python als Sprache ist nicht weniger OOP als Ruby. Was Dir hier aufstösst ist die Standardbibliothek, die mit "zu primitiven" Objekten hantiert.
Ich sehe das anders. Syntax ist wichtig, denn Syntax ist, was die verschiedenen Programmiersprachen unterscheidet und ihnen Charakter gibt. Semantisch äquivalent sind sie auf der einen oder anderen Abstraktionsebene sowieso. Die Syntax aber beeinflusst das Denken und sorgt z.B. dafür, dass bestimmte Sprach- und Entwurfsmuster in Sprache A typisch sind, während sich Sprache B eher für andere Dinge eignet. Wäre Syntax egal, würde jeder einfach Lisp benutzen... ;)

Stefan
BlackJack

Aber mit dem `path`-Modul kann man das doch so schreiben, wie Du es lieber hättest:

Code: Alles auswählen

for item in folder.files():
    content = item.bytes()
    if any(i in content for i in nohits):
        storage.add(item)
Der Unterschied zu der Ruby-Version ist syntaktisch eher marginal: Semantisch ist da IMHO ein viel grösserer Unterschied, weil interne Iteratoren verwendet werden. Die ich nicht mag, weil ich genau "anders herum" denke. :-)
Antworten