Guter Programmierstil

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.
BlackJack

@sprudel: Ich habe bis jetzt nur etwas mit dem Django-ORM und SQLAlchemy gemacht. Angeschaut hatte ich mir noch SQLObject. Für SQLAlchemy (SA) hatte ich mich entschieden, weil es um eine bereits bestehende Datenbank ging und ich den Eindruck hatte, das SA einem da am wenigsten Beschränkungen auferlegt, weil man beliebige Tabellen(teile) auf nahezu beliebige Klassen abbilden kann, wenn es sein muss. Selbst wenn man SQL hat, was nicht auf Klassen abgebildet wird, kann man das mit SA unabhängig von einem konkreten DBMS machen.

Das ist jetzt für Dein Projekt, wo Du die Datenbank selbst entwerfen kannst, natürlich nicht so wichtig. Und Elixir, was auf SA aufbaut, wurde ja auch schon erwähnt. Das macht das deklarieren von Klassen und Beziehungen etwas einfacher als es mit SA "zu Fuss" zu machen.

Deinen zweiten Punkt verstehe ich nicht ganz!? Und eine Datei pro Klasse ist nicht besonders "pythonisch". Eine Datei repräsentiert ein Modul und ein Modul sollte zusammengehörige "Dinge" enthalten. Wenn Du also eine Klasse hast, die eine Transaktion darstellt, und eine die eine einzelne Position auf so einer Transaktion darstellt, macht es durchaus Sinn, die im gleichen Modul zu haben. Ich persönlich stecke solche "Model-Klassen" in der Regel *alle* in ein Modul bis ich den Eindruck habe, dass es deutlich zu viele werden. Dann mach ich mich daran zu überlegen, wie man das sinnvoll in ein oder zwei Module zerlegen kann.

Der letzte Punkt erledigt sich wahrscheinlich von selbst, wenn Du Dir eine Einführung, ein Tutorial oder ein erklärendes Video zu einem der ORMs angeschaut hast.
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Mal ne Frage: Würdet ihr eigentlich das Sqlalchemy direkt in die entsprechenden Klassen (Transaction, Product, usw.) einbauen, oder spezielle Klassen schreiben, die dann zu SQLALchemy gehören, und von dort aus dann die normalen Objekte generieren?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Direkt in die Klassen einbauen. Proxy-Objekte führen schlussendlich dazu dass du Duplikation von Daten hast, wenn du dich nicht schlau anstellst und eine völlig nutzlose Zusatzebene.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Ich weiß, es ist eine blöde Frage, und hier so eigentlich nicht erwünscht.
Aber kann mir hier jemand mal ein grobes Beispiel (gerne auch schemenhaft) einer Beispielklasse (z.B. Transaction) machen, so wie ich das in SQLAlchemy lösen müsste?


Ich stelle mir das im groben (ohne SqlAlchemy) so vor:

Code: Alles auswählen

class Transaction(object):
	products = []
	stornos = []
	notes = []
	payed = False
	

	def addProduct(self,pobj):
		self.products.append(pobj)
	
	def getTotal(self):
		total = 0.00
		for pobj in self.products:
			total += pobj.getPrice()
		return total

Jetzt mal nur als Beispiel.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich sehe 4 Fehler. Oder 2 .. je nachdem wie man das betrachten will.
Willst du die Datenfelder nicht lieber in ein `__init__` schieben und an das Objekt binden?
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Welche Fehler siehst du denn?

Und ja, du hast Recht. Das war jetzt schnell-schnell als Beispiel geschrieben, sowas gehört natürlich ins __init__
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Eben die 4 Attribute. Also nicht so dramatisch wie sich das evtl angehoert hat :roll:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sprudel hat geschrieben:Ich weiß, es ist eine blöde Frage, und hier so eigentlich nicht erwünscht.
Aber kann mir hier jemand mal ein grobes Beispiel (gerne auch schemenhaft) einer Beispielklasse (z.B. Transaction) machen, so wie ich das in SQLAlchemy lösen müsste?
Dazu müßte man doch genau wissen, was diese Klasse leisten soll. Mir ist es im Moment nicht klar (und aus Deinem undokumentierten Ansatz geht das auch imho nicht hervor).

Evtl. magst Du Dir doch zuerst mal Elixir angucken? Trotz der mittlerweile vorhandenen deklarativen Syntax bei SQLAlchemy halte ich Elixir immer noch für einfacher. Schau Dir doch da mal das Tutorial an - einfacher geht der Einstieg kaum (und er ähnelt Deinem Ansatz von der Komplexität hier ziemlich).

Zu Deinem Code fällt mir noch folgendes ein:

- beachte mehr PEP8, Einrückungen sollten 4 Spaces sein, Funktionen und Methoden werden klein_und_mit_underscore benannt. Zwischen Paramertern und dem abgrenzenden Komma immer ein Space

- "addProduct" kapselt letztlich direkt nur die append-Methode einer Liste. Daher kann man sich so etwas auch sparen.

- dieses "getPrice()" einer vermuteten Product-Klasse riecht auch zu sehr nach Java. Anstelle von Gettern und Settern nutzt man in Python eher Properties; sofern Du keine weiteren Tests oder Berechnungen in den Methoden vornimmst, kann man auch einfach direkt auf das Attribut zugreifen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
syntor
User
Beiträge: 88
Registriert: Donnerstag 2. Dezember 2010, 03:56

Hyperion hat geschrieben:- "addProduct" kapselt letztlich direkt nur die append-Methode einer Liste. Daher kann man sich so etwas auch sparen.
Ich denke, dass diese zusätzliche Eben durchaus berechtigt ist: Möchte man zumeist nicht wissen, wenn ein - vermutlich als intern gedachtes, also mit _ prefixen (cooles Wort :) ) - Attribut verändert wird. Im jetzigen Zustand mag das noch nicht erforderlich sein, aber ich denke, dass er gewisse Aktionen ausführen will, sobald ein Produkt hinzugefügt oder entfernt wird, sodass die Daten untereinander konsistent sind. Nur solche Attribute, die ohne Komplikationen geändert werden dürfen, sollten als public markiert werden.
dieses "getPrice()" einer vermuteten Product-Klasse riecht auch zu sehr nach Java. Anstelle von Gettern und Settern nutzt man in Python eher Properties; sofern Du keine weiteren Tests oder Berechnungen in den Methoden vornimmst, kann man auch einfach direkt auf das Attribut zugreifen.
In diesem Fall würde ich dir zustimmen. Besteht die Berechnung des Properties aus mehr als nur ein paar einfachen Statements, so würde ich doch eher zu einer explizizen getter-Methode greifen, um ersichtlich zu machen, dass es eben keine simple Operation ist.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

syntor hat geschrieben: Ich denke, dass diese zusätzliche Eben durchaus berechtigt ist: Möchte man zumeist nicht wissen, wenn ein - vermutlich als intern gedachtes, also mit _ prefixen (cooles Wort :) ) - Attribut verändert wird. Im jetzigen Zustand mag das noch nicht erforderlich sein, aber ich denke, dass er gewisse Aktionen ausführen will, sobald ein Produkt hinzugefügt oder entfernt wird, sodass die Daten untereinander konsistent sind. Nur solche Attribute, die ohne Komplikationen geändert werden dürfen, sollten als public markiert werden.
Ich bin ja auch vom jetzigen Zustand ausgegangen ;-) Wenn solche "Aktionen" tatsächlich notwendig sind, ist eine Methode natürlich erforderlich - aber dann kann ich sie ja immer noch einfügen. Vom Ausdruck "public" in Zusammenhang mit Attributen in Python würde ich übrigens absehen; das riecht zu sehr nach Zugriffskontrolle und weniger nach Konvention. Oben schreibst Du ja auch - wohl bewusst - intern.
In diesem Fall würde ich dir zustimmen. Besteht die Berechnung des Properties aus mehr als nur ein paar einfachen Statements, so würde ich doch eher zu einer explizizen getter-Methode greifen, um ersichtlich zu machen, dass es eben keine simple Operation ist.
Hm... kommt imho drauf an. Bei komplexen und vor allem teuren Operationen handelt es sich ja zumeist auch nicht um das reine Setzen von Attributen, sondern um Funktionalität, die einem eine Klasse bietet. Da benennt man diese ja eh nach dieser. Ich war jedenfalls noch nie in so einer Situation, in der eine property zu komplex war.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Guten Morgen, ihr Lieben.
Zuerst möchte ich mich mal für eure Hilfe Bedanken. Scheine ja wirklich ein ganz schönes Programmierchaos im Kopf zu haben. In der Arbeit wird ja auch eher Quick'n'Dirty verlangt, aber privat möchte ich es schon richtig machen.
So wie es aussieht, habe ich einen ruhigen Arbeitstag, die Computer tuen, und die Benutzer sind zufrieden. Ich werde aller Wahrscheinlichkeit eure Fragen heute also zeitnah beantworten können.

Aber fangen wir mal an:

Dazu müßte man doch genau wissen, was diese Klasse leisten soll. Mir ist es im Moment nicht klar (und aus Deinem undokumentierten Ansatz geht das auch imho nicht hervor).
Ich habe mir das so vorgestellt, dass ein gesamter Verkaufsprozess dort erfasst wird. Also die Produktobjekte, das Kundenkonto, eine Liste von Transaktionen (also z.B. Storno, oder Rabatt, reiner Text und Zeitangabe. Wird wohl wieder ein kleines Objekt werden.
Evtl. magst Du Dir doch zuerst mal Elixir angucken? Trotz der mittlerweile vorhandenen deklarativen Syntax bei SQLAlchemy halte ich Elixir immer noch für einfacher. Schau Dir doch da mal das Tutorial an - einfacher geht der Einstieg kaum (und er ähnelt Deinem Ansatz von der Komplexität hier ziemlich).
Habe mir beide schon etwas angeschaut, werde das aber nochmal gründlich wiederholen.

Zu Deinem Code fällt mir noch folgendes ein:
- beachte mehr PEP8, Einrückungen sollten 4 Spaces sein, Funktionen und Methoden werden klein_und_mit_underscore benannt. Zwischen Paramertern und dem abgrenzenden Komma immer ein Space
Das war jetzt nur kurz in Gedit geschrieben, normalerweise verwende ich Eclipse, und da ist das schon voreingestellt.
- "addProduct" kapselt letztlich direkt nur die append-Methode einer Liste. Daher kann man sich so etwas auch sparen.
Ich habe bereits an die Zukunft gedacht... und ich rechne damit, dass da nochmal was an Funktionalität dazu kommen wird.
- dieses "getPrice()" einer vermuteten Product-Klasse riecht auch zu sehr nach Java. Anstelle von Gettern und Settern nutzt man in Python eher Properties; sofern Du keine weiteren Tests oder Berechnungen in den Methoden vornimmst, kann man auch einfach direkt auf das Attribut zugreifen.
Erwischt, wobei es bei mir eher C++ mit wxWidgets ist. und bei wxWidgets heißen die Methoden nunmal so... und das hat mir eben gefallen.
Werde mir die Pythonstandard antrainieren.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sprudel hat geschrieben: Ich habe mir das so vorgestellt, dass ein gesamter Verkaufsprozess dort erfasst wird. Also die Produktobjekte, das Kundenkonto, eine Liste von Transaktionen (also z.B. Storno, oder Rabatt, reiner Text und Zeitangabe. Wird wohl wieder ein kleines Objekt werden.
Das ist jetzt aber reine Datenhaltung und spiegelt keinerlei Funktionalität wieder. Also ich denke nicht, dass Dir hier jemand daraus etwas bastelt. Ich würde an Deiner Stelle eben einfach selber anfangen, das alles Schritt für Schritt zu implementieren. Du wirst eh das ein oder andere wegschmeißen und dank neuer Erkenntnisse nach implementieren müssen.
Zudem kannst Du nicht mit dem "Integrations"-Objekt anfangen, sondern musst ja zuerst die "Rand"-Klassen implementieren. Ansonsten kannst Du keine (funktionierenden) Relationen definieren.
Habe mir beide schon etwas angeschaut, werde das aber nochmal gründlich wiederholen.
Auch schon mal camelot? Imho könnte das genau das richtige für Dich sein!
- beachte mehr PEP8, Einrückungen sollten 4 Spaces sein, Funktionen und Methoden werden klein_und_mit_underscore benannt. Zwischen Paramertern und dem abgrenzenden Komma immer ein Space
Das war jetzt nur kurz in Gedit geschrieben, normalerweise verwende ich Eclipse, und da ist das schon voreingestellt.
Aber nicht die Benennung ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Noah
User
Beiträge: 15
Registriert: Montag 17. Januar 2011, 08:20

burli hat geschrieben:Es hängt davon ab, wie weit du das treiben willst. Um es einfach nur mal auszuprobieren reichen die vier Klassen für den Anfang
Wir haben bei unserer Warenwirtschaft mittlerweile 378 Tabellen. Ich hoffe nicht, dass es jemand in seiner Freizeit so weit treiben will.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Noah hat geschrieben:Wir haben bei unserer Warenwirtschaft mittlerweile 378 Tabellen. Ich hoffe nicht, dass es jemand in seiner Freizeit so weit treiben will.
Kann es sein, dass es da einer mit der Normalisierung etwas übertrieben hat? :D Man muss nicht für jede Spalte eine eigene Tabelle anlegen. Man kann auch mehrere thematisch zusammengehörende Spalten zusammenfassen :twisted: :twisted: :twisted: :lol:
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@Noah
Jetzt wäre es natürlich noch interessant wieviele Datenssätze auf die 378 Tabellen kommen. Einfach nur um sich auch so wie burli darüber zu amüsieren :D
Aber in meiner Freizeit, habe ich das bisher auch noch nicht so weit getrieben. :mrgreen:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Noah
User
Beiträge: 15
Registriert: Montag 17. Januar 2011, 08:20

Xynon1 hat geschrieben:@Noah
Jetzt wäre es natürlich noch interessant wieviele Datenssätze auf die 378 Tabellen kommen. Einfach nur um sich auch so wie burli darüber zu amüsieren :D
Das kommt auf die Größe des Kunden an. Ich hab mal die Gesamtanzahl auf einer großen Kundendatenbank ermittelt:

33020205

33mio Datensätze, meine güte...

Die verkaufen auch viel kleinzeug, heißt Aufträge mit vielen Positionen. Umsätze ausrechenen dauert dort ein ganzes Wochenende :D

Es sind dort übrigens nur 314 Tabellen gefüllt, da ja nicht jeder Alle funktionen nutzt...
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Nett, noch nicht die Welt, aber dennoch nett, für ein paar Rechnungen, Statistiken und andere kleinere Auswertungen wird es wohl reichen :D
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Noah
User
Beiträge: 15
Registriert: Montag 17. Januar 2011, 08:20

Xynon1 hat geschrieben:Nett, noch nicht die Welt, aber dennoch nett, für ein paar Rechnungen, Statistiken und andere kleinere Auswertungen wird es wohl reichen :D
Ja, wenn ich den bekloppten Rechnungsdruck wieder zum Laufen bekomme... Und von der Statistik will ich gar nicht reden. Ist alles Code der alle VB-Versionen ab 1.0 durchgemacht hat und jetzt auf VB.NET portiert wird.

Am liebsten würde ich ja alles in python neu schreiben, aber das geht verständlicherweise nicht...
Antworten