Einige Fragen zur Programmstruktur und eigenen Modulen

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
heiliga horsd

Hallo,

ich will ein Programm von mir restaurieren bzw. neu schreiben. Der Grundansatz zur Lösung des Problems (Berechnung geometrischer Eigenschaften) bleibt der selbe, aber ich will die ganze Struktur ausmisten und verbessern. Außerdem will ich hier nun auch erstmals größer mit OOP arbeiten. Ich bin nun aber schon bei der Planung auf einige Fragen gestoßen, die evtl. schon vorher geklärt werden könnten.

1. Wie kommt ihr auf die Klassen? Die Frage ist evtl. blöd formuliert, aber ich weiß nicht wie ich das Ausdrücken soll. Wenn ich beispielsweise einen Jeep genauer spezifizieren will, schreibe ich dann die Klasse Auto, die Klasse Geländewagen oder die Klasse Jeep? Soll ich (jetzt konkret) lieber die Klassen 'Rechner' und 'Interface' oder für jede geometrische Figur (Dreieck, Rechteck etc.) eine eigene klasse und dann noch das Interface machen? Letzendlich könnte ich ja auch für die 10 Zeilen Output eine eigene Klasse machen - wann ist also OOP sinnvoll und wann sinnlos? Und ist das "mischen" von OOP und "normalem" Code ein No-Go oder geduldet?

2. Ich würde gerne die Funktionen zur Berechnung auch eigenständig per Konsole verwenden können - nur wie gebe ich dem Programm die Intelligenz zu wissen, ob es nun importiert wird oder als Konsolenanwendung ausgeführt wird und mit den sys.argv-Parameter arbeiten soll?

3. Wie kann ich einen eventuellen mehrsprachensupport (Deutsch, Englisch) möglichst einfach mit Sprachdateien realisieren? (Immerhin kennt nicht jedes Betriebssystem das Wort Kathete und dessen englische Übersetzung)

Die Oberfläche sollte vorübergehend konsolenorientiert sein.


Wäre über Antworten auf meine ganzen, wohl mehr oder weniger dummen Fragen dankbar.

Lg HH
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ad 1) Wenn es sind macht, d.h. wenn man damit den Code verstaendlicher und wartbarer machen kann und es sich um echte Objekte handelt. Deshalb ist das mischen nicht nur geduldet, sondern erwuenscht.
Bei deinem konkreten Problem kann man dir so nicht helfen. Im Prinzip: Ja, aber erzaehl mal, was die Figuren in deiner Anwendung unterscheidet.

Ad 2) "ifmain" und entsprechende Funktionen.

Ad 3) [mod]gettext[/mod]
heiliga horsd

cofi hat geschrieben:Ad 1) Wenn es sind macht, d.h. wenn man damit den Code verstaendlicher und wartbarer machen kann und es sich um echte Objekte handelt. Deshalb ist das mischen nicht nur geduldet, sondern erwuenscht.
Bei deinem konkreten Problem kann man dir so nicht helfen. Im Prinzip: Ja, aber erzaehl mal, was die Figuren in deiner Anwendung unterscheidet.
Naja, das allgemein bekannte halt: Ein Kreis ist rund und hat keine Ecken, beim Dreieck gibt es viele Spezialformen (Gleichschenklig, Rechtwinklig, gleichseitig etc.) und das Viereck hat eine ecke mehr als ein Dreieck und eben auch eine Seite mehr etc.
Alles was man also noch aus der Schule kennt (kennen sollte).
cofi hat geschrieben: Ad 2) "ifmain" und entsprechende Funktionen.
Muss ich dann die Funktionen entsprechend umschreiben oder kann ich bei der "if __name__ (blabla)"-Methode einfach die zu übergebenden Argumente mit dem sys-argv-Werten belegen? Eigentlich schon oder?
cofi hat geschrieben:Ad 3) [mod]gettext[/mod]
Huch, trockenes Futter. Perfekt für den Latein-Unterricht :D Werd mich da mal durchwühlen.

Lg hh

edit:// noch eine kleine Zwischenfrage: Ganz am Anfang Module importieren oder in der Klassendefinition? Habe das nämlich auch schon gesehen...
lunar

Zu 3: Babel als i18n-Bibliothek muss an dieser Stelle auch erwähnt werden.
BlackJack

@heiliga horsd: Ad 1) Klassen fassen Daten und zugehörige Funktionen zu Objekten zusammen. Insofern eignen sich geometrische Figuren ganz gut dafür. Es gibt Daten die eine Figur beschreiben, zum Beispiel den Durchmesser oder den Radius bei einem Kreis, und Operationen darauf wie zum Beispiel Umfang und Flächeninhalt ausrechnen. Ein weiterer Aspekt bei OOP ist Polymorphie, also ganz grob dass man verschiedene Objekte gleich behandeln kann. Zum Beispiel kann man bei allen Figuren mit einer Fläche selbige berechnen. Dadurch kann man Code schreiben, dem es egal ist was für eine konkrete Figur er bekommt, solange sie nur eine Methode zum Berechnen der Fläche hat die bei allen Figuren gleich heisst.

Code: Alles auswählen

def print_info(shape):
    print ('Figur %s hat den Umfang %f und die Fläche %f.'
           % (shape.name, shape.circumference(), shape.area()))
Importieren würde ich am Anfang vom Modul machen, wenn es keine guten Gründe gibt, die dagegen sprechen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

heiliga horsd hat geschrieben:Muss ich dann die Funktionen entsprechend umschreiben oder kann ich bei der "if __name__ (blabla)"-Methode einfach die zu übergebenden Argumente mit dem sys-argv-Werten belegen? Eigentlich schon oder?
Nein, halte die Funktionen allgemein benutzbar und die Verrenkungungen im "ifmain".
BlackJack hat geschrieben:Importieren würde ich am Anfang vom Modul machen, wenn es keine guten Gründe gibt, die dagegen sprechen.
Die guten Gründe gibt es eigentlich nur, wenn ein selten benutzte Funktion grosse Importe nach sich zieht, im Allgemeinen wiegt der Vorteil das zusammengruppiert zu haben hoeher als der kleine Geschwindigkeitsvorteil.
lunar

@cofi: Die "guten Gründe" gibt es immer dann, wenn man die Abhängigkeiten kontrollieren möchte. Schließlich ist alles, was man global importiert, zwingende Abhängigkeit des Moduls. Zu den globalen Imports gehört also nur, was auch wirklich zwingende Abhängigkeit des gesamten Moduls sein darf und soll. Das gilt insbesondere für Module, die nicht Bestandteil der Standardbibliothek und vielleicht sogar noch plattformabhängig sind.

Ich finde es dementsprechend beispielsweise recht unschön, "optparse" oder "getopt" global zu importieren, wenn das Modul eigentlich als Modul und nicht als Programm gedacht ist, und die Kommandozeilenschnittstelle nur zum Testen oder als Demo gedacht ist.
heiliga horsd

cofi hat geschrieben:
heiliga horsd hat geschrieben:Muss ich dann die Funktionen entsprechend umschreiben oder kann ich bei der "if __name__ (blabla)"-Methode einfach die zu übergebenden Argumente mit dem sys-argv-Werten belegen? Eigentlich schon oder?
Nein, halte die Funktionen allgemein benutzbar und die Verrenkungungen im "ifmain".
OK also ich bin mir nicht ganz sicher was du meinst ich denke mit einem Codeschnipsel sähe das besser aus, das ist jetzt nur kurz zusammengetippt also bitte nicht allzustreng auf fehler achten :wink:

Code: Alles auswählen

import sys

def Flaeche(a, b):
    print("Fläche", a*b)
    

if __name__ == '__main__':
    a = sys.argv[1]
    b = sys.argv[2]
    Flaeche(a, b)
So würde ich das halt grob vereinfacht machen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dir sollte klar sein, dass das auch so laeuft:

Code: Alles auswählen

import sys

def Flaeche(a, b):
    print("Fläche", a*b)
    

if __name__ == '__main__':
    b = sys.argv[1]
    a = sys.argv[2]
    Flaeche(b, a)
oder gleich:

Code: Alles auswählen

...
if __name__ == '__main__':
    Flaeche(sys.argv[1], sys.argv[2])
Ich meinte damit, dass du dich nicht in deinen Funktionen anpassen sollst, sondern eben die eingaben von der Kommandozeile den entsprechenden Funktionen uebergibst und u.U. vorbereitest etc, ich glaube das hast du schon richtig verstanden.

@lunar: Ja bedingte Importe sind unbedingt zu erwaehnen, aber die sollten eben auch auf oberster Ebene bzw an typischen Stellen sein und nicht tief versteckt.
lunar

@cofi: Über den Ort, an dem bedingte bzw. optionale Importe stehen sollte, lässt sich jetzt trefflich streiten. Ich persönlich finde den Import an der Stelle, an welcher sie wirklich benötigt werden, in den meisten Fällen eleganter als Konstrukte der Art:

Code: Alles auswählen

try:
    import foobar
except ImportError:
    foobar = None
…
def some_func():
    if foobar:
        foobar.do_something()
Insbesondere, wenn der optionale Import nur an wenigen Stellen verwendet werden. "subprocess" aus der Standardbeispiel ist ein schönes Beispiel dafür, wie man es nicht machen sollte.
heiliga horsd

cofi hat geschrieben:Dir sollte klar sein, dass das auch so laeuft:

Code: Alles auswählen

import sys

def Flaeche(a, b):
    print("Fläche", a*b)
    

if __name__ == '__main__':
    b = sys.argv[1]
    a = sys.argv[2]
    Flaeche(b, a)
oder gleich:

Code: Alles auswählen

...
if __name__ == '__main__':
    Flaeche(sys.argv[1], sys.argv[2])
Aber nur bei der Fläche oder? Immerhin kann ich ja bei Differenzen bspw. nicht drehen wie ich lustig bin, dann kommt Quatsch als Ergebnis raus!
cofi hat geschrieben: Ich meinte damit, dass du dich nicht in deinen Funktionen anpassen sollst, sondern eben die eingaben von der Kommandozeile den entsprechenden Funktionen uebergibst und u.U. vorbereitest etc, ich glaube das hast du schon richtig verstanden.
Ja so war es auch anfangs gemeint. Meine Funktionen sollen nur rechnen, ich versuche die Daten immer gleich (im Sinne von 'auf die selbe Art und Weise') zu übergeben.
cofi hat geschrieben: @lunar: Ja bedingte Importe sind unbedingt zu erwaehnen, aber die sollten eben auch auf oberster Ebene bzw an typischen Stellen sein und nicht tief versteckt.
Da hab ich wohl eine schöne Diskussion losgetreten. Ich denke ich werde die importe global machen, immerhin sind die genannten Gründe wohl eher immer persönliche Geschmackssache bzw. für mich von geringer Bedeutung.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

heiliga horsd hat geschrieben:Aber nur bei der Fläche oder? Immerhin kann ich ja bei Differenzen bspw. nicht drehen wie ich lustig bin, dann kommt Quatsch als Ergebnis raus!
Ich hab wohl ins schwarze getroffen. `a` und `b` wird im Funktionsaufruf durch die Reihenfolge (oder Schluesselwortargumente) bestimmt, nicht durch den Bezeichner an den es gebunden wird.
Die Reihenfolge war in allen 3 Snippets dieselbe.
heiliga horsd

Ah, das habe ich wohl übersehen, ich dachte du hast die Zeilen generell vertauscht. Also dass die Argumente der Reihenfolge wie sie rein kommen nach zugeordnet werden wusste ich... Sorry :oops:
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Ich verwende lieber

Code: Alles auswählen

sys.argv[-2], sys.argv[-1]
für die beiden letzten Elemente. So ist man auch sicher, dass es funktioniert, egal ob jetzt das Skript mit oder ohne vorangestellten Pythonbefehl aufgerufen wird.
BlackJack

@mkesper: Verstehe ich nicht!?

Code: Alles auswählen

#!/usr/bin/env python
import sys
print sys.argv

Code: Alles auswählen

bj@s8n:~$ python test.py a b
['test.py', 'a', 'b']
bj@s8n:~$ ./test.py a b
['./test.py', 'a', 'b']
Der Python-Interpreter nimmst sich selbst aus 'argv' raus.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

heiliga horsd hat geschrieben:2. Ich würde gerne die Funktionen zur Berechnung auch eigenständig per Konsole verwenden können - nur wie gebe ich dem Programm die Intelligenz zu wissen, ob es nun importiert wird oder als Konsolenanwendung ausgeführt wird und mit den sys.argv-Parameter arbeiten soll?
Hallo HH!

Das mit ``if __name__ == "__main__"`` weißt du ja schon. Nur würde ich bei deinem Programm nicht direkt auf ``sys.argv`` zugreifen wollen. Nimm lieber das Modul optparse um die Kommandozeilenargumente zu parsen und dir auch die Syntax/Hilfe zu den Argumenten anzeigen zu lassen.

heiliga horsd hat geschrieben:3. Wie kann ich einen eventuellen mehrsprachensupport (Deutsch, Englisch) möglichst einfach mit Sprachdateien realisieren?
gettext und z.B. PoEdit


mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

BlackJack hat geschrieben:@mkesper: Verstehe ich nicht!?

Code: Alles auswählen

#!/usr/bin/env python
import sys
print sys.argv

Code: Alles auswählen

bj@s8n:~$ python test.py a b
['test.py', 'a', 'b']
bj@s8n:~$ ./test.py a b
['./test.py', 'a', 'b']
Der Python-Interpreter nimmst sich selbst aus 'argv' raus.
Ich meine, dass ich da mal Probleme hatte im Zusammenhang mit Debugging in Eclipse (keine Ahnung mehr ob's Eclipse war, jedenfalls benutze ich seither dieses Verfahren).
Davon unabhängig hat Gerold natürlich Recht, direkter Zugriff auf sys.argv ist eben "Quick and Dirty".
heiliga horsd

@gerold:

Danke dir, von dem Modul hatte ich keine Ahnung. Sieht ja beim Überfliegen doch recht mächtig aus und hat schon fast Tutorial-Charakter.

Nun ist mir noch was bezüglich des Mehrsprachensupports eingefallen. Wenn ich bspw. Englisch auch anbieten will, muss ich auch auf deren Gewohnheiten eingehen, was letzendlich heißt: Bogenmaß. Letzendlich würden sich da also wieder ein paar Berechnungen ändern. Ist wohl am besten eine de-Funktion und eine en_us-Funktion zu schreiben, oder?
Antworten