Seite 1 von 1

Anregungen zur Objektorientierung

Verfasst: Freitag 28. Dezember 2007, 14:42
von DocFisher
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

Re: Anregungen zur Objektorientierung

Verfasst: Freitag 28. Dezember 2007, 15:10
von Leonidas
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.

Verfasst: Freitag 28. Dezember 2007, 15:34
von DocFisher
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

Verfasst: Freitag 28. Dezember 2007, 16:34
von sma
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

Verfasst: Freitag 28. Dezember 2007, 17:06
von DocFisher
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

Verfasst: Freitag 28. Dezember 2007, 17:16
von Leonidas
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'>

Verfasst: Freitag 28. Dezember 2007, 17:19
von 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?

Verfasst: Freitag 28. Dezember 2007, 17:56
von DocFisher
@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

Verfasst: Samstag 29. Dezember 2007, 12:08
von sma
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

Verfasst: Samstag 29. Dezember 2007, 12:29
von 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. :-)