Seite 2 von 3

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 10:44
von Sirius3
@NoPy: wenn Du Deinen eigenen Code nicht mehr verwenden kannst, dann ist dort das Problem zu suchen. Was Du beschreibst ist nicht programmieren, sondern raten. In der Art: ich habe hier 5 Parameter und der Compiler sagt mir schon, welche ich in welcher Reihenfolge der Funktion übergeben muß, weil sonst passt es ja vom Typ her nicht. Das kann nicht funktionieren. Typfehler sind nämlich das seltenste, was man an Fehlern macht. Die meisten kann kein Compiler entdecken, denen kann man nur auf die Spur kommen, wenn man strukturiert programmiert und weiß was man macht.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 10:48
von BlackJack
@NoPy: Man testet Code ja, da fallen eigentlich die Probleme die ein Compiler bei statisch typisierten Programmiersprachen findet, auf und noch mehr, weswegen ja auch bei solchen Sprachen getestet wird.

Wenn Du zwei ähnliche Dinge gelöst hast und absehbar ist das diese Lösungen so oder so ähnlich auch für das nächste Projekt sinnvoll sind, *dann* würde ich hingehen und den vorhandenen Code so umschreiben, dass eine gemeinsam benutzbare Bibliothek entsteht. Halt das was an Gemeinsamkeiten vorhanden ist, aus dem Code herausziehen. Oder wenn man etwas neues anfängt und feststellt das man etwas ähnliches schon mal gemacht hat, kann man den Zeitpunkt nutzen den entsprechenden Code aus dem alten Projekt als Bibliothek aufzubereiten.

Die Schnittstellen sind bei Python doch auch ”reglementiert”, nur mehr durch Dokumentation und nicht durch statische Typisierung, was ja auch nur ein Teil der Schnittstelle ist. Wichtig ist ja auch und gerade das Verhalten der Objekte.

Bei der Routenplaner-Geschichte würde ich eine Schnittstelle für den Routenplaner definieren, also beispielsweise das so ein Objekt eine `address_distance()`-Methode haben muss die zwei Adressen als Argumente bekommt und eine Route mit Attributen wie Entfernung und Zeit zurück liefert. Dann schreibt man für jeden konkreten Routenplaner eine Klasse die diese Schnittstelle bietet. Das würde ich in jeder objektorientierten Programmiersprache so angehen, nicht nur in Python.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 11:25
von NoPy
Sirius3 hat geschrieben:@NoPy: wenn Du Deinen eigenen Code nicht mehr verwenden kannst, dann ist dort das Problem zu suchen.

genau, deswegen frage ich ja, wie man es richtig macht.
Sirius3 hat geschrieben:Was Du beschreibst ist nicht programmieren, sondern raten. In der Art: ich habe hier 5 Parameter und der Compiler sagt mir schon, welche ich in welcher Reihenfolge der Funktion übergeben muß, weil sonst passt es ja vom Typ her nicht. Das kann nicht funktionieren.

Doch, genau so. Dafür ist der Compiler ja da. Ich weiß bei einer Methode, die "NäheZusammen" heißt, schon, dass ich eine Nadel, ein Muster, vielleicht eine Geschwindigkeit und eine Fadenstärke übergeben muss, warum sollte ich mir die Reihenfolge der Parameter oder den Typ der selben merken, wenn der Compiler das viel besser kann?
Sirius3 hat geschrieben:Typfehler sind nämlich das seltenste, was man an Fehlern macht. Die meisten kann kein Compiler entdecken, denen kann man nur auf die Spur kommen, wenn man strukturiert programmiert und weiß was man macht.

Dann muss ich fürchterlich genial sein, denn solche Fehler, wie Du sie beschreibst, mache ich vielleicht einmal im Jahr. Den Compiler meine Tipp- und Typfehler suchen lasse ich hingegen manchmal im Minutentakt. Und die IDE die verfügbaren Methoden suchen lasse ich im Grunde bei jedem dritten Wort.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 12:01
von NoPy
BlackJack hat geschrieben:@NoPy: Man testet Code ja, da fallen eigentlich die Probleme die ein Compiler bei statisch typisierten Programmiersprachen findet, auf und noch mehr, weswegen ja auch bei solchen Sprachen getestet wird.
Mein Problem ist nicht so sehr das Testen, auch wenn mich untypisierte Sprachen stärker zwingen, Testklassen/Methoden/Module zu schreiben, als typisierte, weil das Arsenal an auftretenden Fehlern zumindest um diese bereichert worden ist, die sonst Compiler oder Linker herausfischen. Und man muss aus dem gleichen Grunde viel mehr Fälle abdecken, weil etwas, das sich wie eine Liste verhält, keine Liste sein muss und - wie bei mir ja gesehen - nach einmaligem durchiterieren verschwunden sein kann.
Mein Problem ist, dass ich es nicht hinbekomme, intuitiv bedienbare Schnittstellen zu schaffen, die ich wiederverwenden kann, ohne permanent Dokumentationen zu lesen, zu erzeugen, zu ergänzen. Wenn ein Modul fertig ist, dann soll es nach Möglichkeit fertig und wiederbenutzbar sein, für möglichst alle Zwecke, die sich im Kontext dieses Moduls befinden (Modul meint hier allgemein alles, von einer Funktion über eine Klasse, ein Modul bis zu einer Bibliothek)
Und wenn ich die verwendeten Farben eines Bildes als Set, als Liste, als Tupel, als Wörterbuch und als Klasse haben kann, dann müsste sie theoretisch auch mit all diesen Konstrukten spielen. Das wiederum heißt, ich muss irgendwie all diese Fälle unterscheiden (oder gemeinsame Schnittstellen nutzen). Und wenn die Farben als Text, als RGB, als CYMB oder sonstwie gespeichert werden, dann muss ich auch irgendwie darauf reagieren.

Und wenn eben der Mensch, der openpyxl geschrieben hat, sich gedacht hat, er müsse den Bereich als Generator zurückgeben, dann macht er das eben. Und ich muss es dann eben wissen. Und wenn es nicht in der Dokumentation zu erkennen ist, dann muss ich ausprobieren. Und dabei kann ich nur hoffen, dass sich diese Klasse dann immer gleich verhält und nicht bei kleineren Datenmengen Listen und bei größeren Datenmengen Generatoren verwendet. Im Gewissen Sinne bin ich dann nämlich der, der die Testmethoden zu diesem Modul schreibt, zumindest, soweit ich diese Funktionen benutzen will.

Daher meine Frage: Wenn ich nach oben stabil sein will und die eigentliche Geschäftslogik, die eine simple ist, so simpel halten möchte, wie genau kapsele ich dann sinnvoll all das, was sich irgendjemand mal ausgedacht hat (dieser irgendjemand kann auch ich sein) und von dem ich nur Bruchstücke weiß?
BlackJack hat geschrieben:Wenn Du zwei ähnliche Dinge gelöst hast und absehbar ist das diese Lösungen so oder so ähnlich auch für das nächste Projekt sinnvoll sind, *dann* würde ich hingehen und den vorhandenen Code so umschreiben, dass eine gemeinsam benutzbare Bibliothek entsteht.

Das ist Wiederverwendbarkeit in Deinen Augen? Ich würde das ja so angehen, dass ich die vorhandenen Module benutze und in ein weiteres spezifisches einfließen lasse. Dann muss ich mir nämlich auch keinen Kopf mehr machen, welche Auswirkungen das Redesign auf von den Ursprungsmodulen abhängigen Code hat.
BlackJack hat geschrieben:Halt das was an Gemeinsamkeiten vorhanden ist, aus dem Code herausziehen. Oder wenn man etwas neues anfängt und feststellt das man etwas ähnliches schon mal gemacht hat, kann man den Zeitpunkt nutzen den entsprechenden Code aus dem alten Projekt als Bibliothek aufzubereiten.
Allen ernstes? Du kaufst immer die ganze Kuh, wenn Du einen Becher Milch trinken willst?
BlackJack hat geschrieben:Die Schnittstellen sind bei Python doch auch ”reglementiert”, nur mehr durch Dokumentation und nicht durch statische Typisierung, was ja auch nur ein Teil der Schnittstelle ist. Wichtig ist ja auch und gerade das Verhalten der Objekte.
In meinen Augen widerspricht sich das. Wenn ich im Sinne von OOP nur atomare Funktionen mit sprechenden Namen verwende, dann ist das einzige, was ich noch beschreiben muss, die Liste der möglichen Parameter. Und "reglementiert" würde für mich bedeuten, dass ein Regelversto0 geahndet würde. Jedes meiner Python- Schnipsel war lauffähig ohne das geringste Maß an Dokumentation.
Es ist eigentlich vielmehr eine Best Practice beschrieben, als eine Regel festgelegt.
BlackJack hat geschrieben:Bei der Routenplaner-Geschichte würde ich eine Schnittstelle für den Routenplaner definieren, also beispielsweise das so ein Objekt eine `address_distance()`-Methode haben muss die zwei Adressen als Argumente bekommt und eine Route mit Attributen wie Entfernung und Zeit zurück liefert. Dann schreibt man für jeden konkreten Routenplaner eine Klasse die diese Schnittstelle bietet. Das würde ich in jeder objektorientierten Programmiersprache so angehen, nicht nur in Python.
Na klar, bei jeder anderen Programmiersprache weiß ich auch, wie ich das machen soll, nur nicht in python.

google maps API Dokumentation: https://developers.google.com/maps/docu ... ions/intro
python json Dokumentation: https://docs.python.org/2/library/json.html

Und Du kannst allein aus diesen Dokumentationen erkennen, an welchen Stellen sich die Entfernungen/Zeiten wie auslesen lassen?
https://maps.googleapis.com/maps/api/di ... n=Montreal
liefert jede Menge JSON- Match, es war nervtötendes Basteln, an welchen Stellen das als was aufgelöst wurde. Gut, das ist nun fertig, aber ich möchte es eigentlich erst wieder anfassen, wenn entweder meine Bedürfnisse sich geändert haben, oder google die Schnittstelle geändert hat.

Daher würde ich eben gern wissen, wie ich wrapper- KLassen (auch für meine eigenen) schreiben kann. Meine Frage von vorhin bleibt bestehen, gern auch ein entsprechendes Tutorial.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 13:01
von pillmuncher
@NoPy: Schon deine erste Frage hier im Forum war, wie man den Datentyp einer Variablen deklarieren kann. Du scheinst immer noch nicht darüber hinweg gekommen zu sein, dass das in Python eben nicht geht. Und dass es nicht geht, ist kein Defizit der Sprache Python, sondern eine bewusste Designentscheidung, die sich mindestens bis ins Jahr 1958 zurückverfolgen lässt, als LISP erfunden wurde. Ich weiß auch nicht, wie oft ich noch wiederholen muss, dass Python eine Art Lisp mit weniger Klammern ist, kein C/C++/C#/Java mit weniger Klammern. Statt auf Compilermeldungen verlässt man sich in Python auf die Dokumentation und auf Tests und als Belohnung erhält man Code, der manchmal sogar um Größenordnungen kürzer und prägnanter ist, als in den genannten Bondage & Discipline Sprachen. Wie man damit umgeht, muss man sicherlich lernen, und das geht IMO am besten, wenn man sich den Code erfahrener Pythonistas ansieht. Große Teile der Standardlib sind in Python programmiert, weswegen das ein guter Startpunkt wäre. Außerdem sollte man PEP8 und PEP20 beherzigen.

Auf deine Frage bzgl. Listen/Tupel/Generatoren: alle diese implementieren das Iterable Protokoll, deswegen sollte man sich darauf stützen. Statt Datenstrukturen zu manipulieren, würde man Transformationen auf Daten implementieren und diese zu Pipelines zusammenfügen. Die Mittel der Wahl dazu sind List/Set/Dictionary Comprehensions und Generator Expressions.

Wenn du ein konkretes Codebeispiel hast, das die Problematik verdeutlicht, vor der du stehst, kann man vielleicht helfen.

Achja: Python ist nicht untypisiert, sondern stark dynamisch typisiert.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 13:30
von BlackJack
@NoPy: Falls Du Compiler wirklich als Werkzeuge ansiehst Dir die Argumentreihenfolge zu erklären und ihn im Minutentakt als Fehlersuche verwendest, dann sind dynamisch typisierte Programmiersprachen nichts für Dich. Denn da musst Du Dir tatsächlich merken wie die API aussieht, denn der Compiler hilft Dir da nicht. Da musst Du zwangsläufig Deine Arbeitsweise ändern. Was die Sprache nicht an statischen Informationen einfach verfügbar macht, kann eine IDE nicht berücksichtigten. Ein bisschen was geht, aber halt nicht alles.

Bist Du sicher das Du nur einmal im Jahr einen Fehler machst der nicht auf einen falschen Typ basiert, oder testest Du einfach nur nicht systematisch und stösst deshalb seltener auf diese Fehler?

Python ist übrigens nicht untypisiert sondern dynamisch und stark typisiert.

Dokumentieren ist ein gutes Stichwort. Das sollte man tun. Und aktuell halten. Sollte man allerdings auch bei statisch typisierten Programmiersprachen so machen. ”Intuitiv” benutzbar ist nämlich so eine Sache.

Das Beispiel mit den Farben, da würde ich wieder sagen dieser „ich muss schauen wie die Farben übergeben wurden“-Ansatz und dann entscheiden was ich damit mache einfach von Anfang an falsch. Man muss einfach nur Vorschreiben wie die Farben auszusehen haben, möglichst *einen* (Duck-)Typ. Man muss nicht zig Sachen unterscheiden sondern einfach *eine* Vorgeben.

Wenn Du von openpyxl einen Bereich als Liste benötigst, dann mach einfach eine Liste draus. Das funktioniert auch mit einer Liste. Ganz allgemein mit jedem iterierbaren Objekt. Das wäre dann die einzige Eingenschaft die auch in Zukunft erfüllt sein muss.

Das was ich beschrieben habe ist mein pragmatischer Ansatz zur Wiederverwendbarkeit. Ich versuche nicht alles von vornherein als universell wiederverwendbare Bibliothek zu schreiben weil ich festgestellt habe das ich in sehr vielen Fällen dabei Energie aufwände die unnötig ist weil's dann doch nicht wiederverwendet wird. Ich schreibe lieber konkrete, anwendbare Lösungen als mir irgendwelche komplexen APIs auzudenken die wenn sie dann auf die Realität treffen doch nicht 100% passen und dann auch nur einmal ein Teil davon verwendet wird. You Ain't Gonna Need It (YAGNI) ist hier das Stichwort. Sofern bestimmte Flexibilität nicht einfach und zum ”Nulltarif” (oder nahe dran) implementiert werden kann, baue ich sie nicht ein wenn ich nicht weiss oder konkret absehen kann das sie benötigt werden wird.

Das das Extrahieren oder Verändern von Bibliotheken Einfluss auf die alten, bestehenden Projekte hat sehe ich als Vorteil, denn die dienen damit mit ihren Tests und auch im Betrieb als Überprüfung der Bibliothek und sichern/erhöhen damit die Qualität. Wenn ich mir einen Kopf um die Auswirkungen eines Redesigns in Bezug auf den bereits abhängigen Code machen muss, dann muss ich mir naturgemäss auch mehr Gedanken um die API machen und dadurch entsteht dann hoffentlich eine bessere als wenn die nur für ein Projekt zugeschnitten ist. Das macht sich dann beim nächsten Projekt bezahlt das diese Bibliothek benutzt.

Das mit der Kuh und der Milch habe ich nicht verstanden. Also ich kenne die Redewendung, bekomme den Bezug zu dem Zitat aber nicht hin.

”Reglementiert” hatte ich nicht umsonst in Anführungszeichen gesetzt. Es wird halt kein Regelverstoss geahndet. Obwohl: Doch, das Programm funktioniert nicht korrekt wenn man sich nicht an die Regeln hält.

Wenn Du das vorgehen bei anderen Programmiersprachen weisst, wo liegt dann das konkrete Problem bei Python?

Was hat denn die Maps-API mit Python zu tun? Aus den JSON-Antworten herauszufinden wo da welche Information steht ist doch ein Problem das sich völlig unabhängig von der für die Anfrage verwendeten Programmiersprache stellt.

Ich sehe bei der API jetzt auch kein nervtötendes basteln, das Format ist doch auf der verlinkten Webseite recht ausführlich beschrieben.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 13:46
von NoPy
pillmuncher hat geschrieben:@NoPy: Schon deine erste Frage hier im Forum war, wie man den Datentyp einer Variablen deklarieren kann. Du scheinst immer noch nicht darüber hinweg gekommen zu sein, dass das in Python eben nicht geht. Und dass es nicht geht, ist kein Defizit der Sprache Python, sondern eine bewusste Designentscheidung, die sich mindestens bis ins Jahr 1958 zurückverfolgen lässt, als LISP erfunden wurde. Ich weiß auch nicht, wie oft ich noch wiederholen muss, dass Python eine Art Lisp mit weniger Klammern ist, kein C/C++/C#/Java mit weniger Klammern. Statt auf Compilermeldungen verlässt man sich in Python auf die Dokumentation und auf Tests und als Belohnung erhält man Code, der manchmal sogar um Größenordnungen kürzer und prägnanter ist, als in den genannten Bondage & Discipline Sprachen. Wie man damit umgeht, muss man sicherlich lernen, und das geht IMO am besten, wenn man sich den Code erfahrener Pythonistas ansieht. Große Teile der Standardlib sind in Python programmiert, weswegen das ein guter Startpunkt wäre. Außerdem sollte man PEP8 und PEP20 beherzigen.
Nun ja, darüber bin ich schon lange weg. Dass Programmierung in python für mich persönlich so ist, als hätte man mir einen Arm auf den Rücken gebunden und an der anderen Hand Daumen und Ringfinger zusammengeklebt, werde ich nicht an den Design- Entscheidungen nicht rütteln, wozu auch.
Das Beherzigen von PEP* ist für mich nicht sonderlich hilfreich. Im Wesentlichen stehen dort viele Dinge zur Quellcodeformatierung (was ich bislang getrost einem Code- Beautifier überlassen durfte) und ein paar seeeeeehhhhr allgemeine Ratschläge á la
"Wähle den einfachsten Weg"
"Mach es gleich"
...
Das ist für mich wirklich so hilfreich, wie ein Nuckel.
pillmuncher hat geschrieben:Auf deine Frage bzgl. Listen/Tupel/Generatoren: alle diese implementieren das Iterable Protokoll, deswegen sollte man sich darauf stützen. Statt Datenstrukturen zu manipulieren, würde man Transformationen auf Daten implementieren und diese zu Pipelines zusammenfügen. Die Mittel der Wahl dazu sind List/Set/Dictionary Comprehensions und Generator Expressions.
Ist schon klar, statt
x.tu_was_mit_ding(y)
y_neu = x.baue_neues_ding_aus_y(y)
und so weiter.
pillmuncher hat geschrieben:Wenn du ein konkretes Codebeispiel hast, das die Problematik verdeutlicht, vor der du stehst, kann man vielleicht helfen.
Weiter oben: Wie baue ich eine Wrapper- Klasse, um "fremde" oder "alte" Klassen an neue Herausforderungen anzupassen?
Wiederholung:

Code: Alles auswählen

class A(objekt):
    def __init__(self,a,b,c,d):
        self.x=[a,b,c,d]
    def Fegen(self):
        pass
    
class WrapperA(???):
    ???
    def SauberMachen(self):
        self.Fegen()
        
MeinA = WrapperA(1,2,3,4,5,6,7,8)
pillmuncher hat geschrieben:Achja: Python ist nicht untypisiert, sondern stark dynamisch typisiert.
schon klar

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 14:03
von BlackJack
Wirklich konkret ist das abstrakte Beispiel ja nicht. Da würde ich wahrscheinlich einfach das hier machen:

Code: Alles auswählen

class A(object):

    def __init__(self, a, b, c, d):
        self.x = [a, b, c, d]

    def fegen(self):
        pass


class WrapperA(A):

    saubermachen = A.fegen
Wähle den einfachsten Weg halt. :-) (Noch einfacher wäre vielleicht ein „monkey patch“, aber auch deutlich unsauberer.)

Eine allgemeingültige Antwort kann man aber auch gar nicht geben *und* ich sehe auch immer noch nicht wie sich an dieser Stelle Python von anderen OOP-Programmiersprachen unterscheidet. Du sagst ja in anderen Sprachen wäre das kein Problem. Welche Sprachen sind das denn? Und wie würdest Du es dort machen?

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 14:39
von pillmuncher
Das Problem verstehe ich immer noch nicht. Besser gesagt, ich verstehe nicht, inwiefern sich Python hier anders verhalten soll, als andere OOP-Sprachen. Wenn etwas aus Teilen besteht und eine polymorphe Operation auf diesen Teilen ausgeführt werden soll, obwohl die Operation nicht für alle Teile definiert ist, hilft einem statische Typisierung auch nicht weiter. Man muss immer einen Adapter schreiben. Oder einen Visitor, mit allen Problemen die das mit sich bringt. Oder Multimethoden. Das ist aber nichts Python-spezifisches.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 15:09
von NoPy
BlackJack hat geschrieben:Wirklich konkret ist das abstrakte Beispiel ja nicht. Da würde ich wahrscheinlich einfach das hier machen:

Code: Alles auswählen

class A(object):
    def __init__(self, a, b, c, d):
        self.x = [a, b, c, d]

    def fegen(self):
        pass

class WrapperA(A):

    saubermachen = A.fegen

#Das heisst also, mit 
MyA = WrapperA(1,2,3,4)
#erzeuge ich ein Objekt, bei dem 
print MyA.x
#[1,2,3,4] liefert
MyA.fegen() #ruft pass auf
MyA.saubermachen() #ruft fegen auf
Wähle den einfachsten Weg halt. :-) (Noch einfacher wäre vielleicht ein „monkey patch“, aber auch deutlich unsauberer.)
Und um den Konstruktor anzupassen, müsste also folgendes funktionieren

Code: Alles auswählen

class A(object):
    def __init__(self, a, b, c, d):
        self.x = [a, b, c, d]

class WrapperA(A):
    def __init__(self, a, b, c, d, e):
        A.__init__(a,b,c,d)
        self.x.append(e)
BlackJack hat geschrieben:Eine allgemeingültige Antwort kann man aber auch gar nicht geben *und* ich sehe auch immer noch nicht wie sich an dieser Stelle Python von anderen OOP-Programmiersprachen unterscheidet. Du sagst ja in anderen Sprachen wäre das kein Problem. Welche Sprachen sind das denn? Und wie würdest Du es dort machen?
In Pascal sähe das so aus:

Code: Alles auswählen

  { TA }
  TA = class(TObject)
    x : array of string;
    constructor Create(const a,b,c,d:string);
    procedure Fegen();
  end;

  { TWrapperA }
  TWrapperA = class(TA)
    constructor Create(const a,b,c,d,e:string);
    procedure SauberMachen();
  end;

{ TA }
constructor TA.Create(const a, b, c, d: string);
begin
  setlength(x,4);
  x[0]:=a;
  x[1]:=b;
  x[2]:=c;
  x[3]:=d;
end;

procedure TA.Fegen;
begin

end;

{ TWrapperA }
constructor TWrapperA.Create(const a, b, c, d, e: string);
begin
  inherited Create(a,b,c,d);
  setlength(x,5);
  x[4]:=e;
end;

procedure TWrapperA.SauberMachen;
begin
  Fegen()
end;
Ist natürlich länger bzw. inhaltlich unflexibler, aber dafür schreit der Compiler auch sofort laut los, wenn ich versuche, als Parameter eine Zahl zu übergeben.
Also kann ich sofort mühelos explizit konvertieren.

Aber ich denke, es ist klar geworden, dass ich nicht versuche, hier einen Glaubenskrieg der Programmiersprachen zu entfachen. Es ist eher so, dass ich versuche, dieses nicht als Konflikt zu verstehen. Ich möchte einfach lernen, effektiv mit dem Werkzeug python umzugehen. Sicher hat sich auch meine persönliche Stoßrichtung geändert, ich schreibe nicht mehr Programme für irgendwelche Nutzer, auch muss ich in der Regel nicht mehr über Multithreading und Nebenläufigkeit, GUI oder ähnliches diskutieren oder mich mit Kollegen abstimmen. Dafür schreibe ich mir jetzt meine eigenen Tools. 100 Stück pro Jahr, jedes mit eigener Zielsetzung, aber großen Überschneidungen hinsichtlich der verwendeten "Schnittstellen" (FME, Excel, Oracle, Access, QGIS, DBF ...)

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 15:41
von pillmuncher
@NoPy: Ich verstehe es immer noch nicht. Einerseits möchtest du eine polymorphe Operation anwenden auf Dinge, die diese Operation nicht bereitstellen und deswegen adaptiert werden müssen. So weit, so OOP. Andererseits vermisst du die statische Typprüfung von Methodenargumenten, was mit der ersten Fragestellung gar nichts zu tun zu haben scheint. Was ist denn nun dein Problem?

Und: Python ist auch mehr LISP als Pascal. Pascal und C sind eh dieselbe Sprache.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 16:07
von NoPy
pillmuncher hat geschrieben:@NoPy: Ich verstehe es immer noch nicht. Einerseits möchtest du eine polymorphe Operation anwenden auf Dinge, die diese Operation nicht bereitstellen und deswegen adaptiert werden müssen. So weit, so OOP. Andererseits vermisst du die statische Typprüfung von Methodenargumenten, was mit der ersten Fragestellung gar nichts zu tun zu haben scheint. Was ist denn nun dein Problem?

Und: Python ist auch mehr LISP als Pascal. Pascal und C sind eh dieselbe Sprache.
Noch mal: Ich will keinen Glaubenskrieg anfangen über Pascal/C/LISP/Python/Prolog/Forth/Basic
Alle haben Gemeinsamkeiten und Unterschiede, Vorteile und Nachteile

Ich will schlicht lernen, wie ich am effizientesten python nutze, um meine Probleme zu lösen. Es ist mir letztlich egal, wie das oder die Konzepte heißen und funktionieren.
Ich muss dazu die Konzepte lernen - das ist mir ganz klar.
Ich muss meine Aufgaben lösen - das sollte Euch klar sein.

Ich habe einen anderen Background und vor diesem Hintergrund bin ich einiges an Features gewöhnt, die nun zwangsläufig wegfallen. Dafür gewinne ich Freiheiten und neue Features. So ist das eben.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 16:38
von pillmuncher
@NoPy: Um dir helfen zu können, muss ich wissen, welches Problem du hast. Ich habe es bis jetzt nicht verstanden.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 17:29
von NoPy
pillmuncher hat geschrieben:@NoPy: Um dir helfen zu können, muss ich wissen, welches Problem du hast. Ich habe es bis jetzt nicht verstanden.
  • -Das ursprüngliche Thema "Fehlersuche" bezog sich darauf, dass ein von mir benutztes Modul (openpyxl) generatoren zurückgeliefert hat, so dass für mich virtuell die Elemente nach dem ersten Ansehen verschwanden ("Burn after Reading").
    -Das führte im Weiteren dazu, dass ich mich informieren wollte, wie man python- like damit umgeht, wenn die eigenen Module etwas verwenden möchten, aber nur prinzipiell und nicht genau dazu passen.
    -Die Antwort von BlackJack war letztlich: Lieber Wrapperklassen, die die verwendeten Module kapseln, als die benutzenden Klassen variabel zu gestalten.
    -Seit dem versuche ich, die Mechanismen zu lernen, wie man python- like wrapper- Klassen baut.
Eigentlich ist für mich die Frage mittlerweile beantwortet (mal abgesehen von meiner letzten Reaktion auf die Antwort von BlackJack).
Im Moment versuche ich aus den vielen Versuchen, mir die Sprache python besonders schmackhaft zu machen, mir die Krumen Information herauszupicken, mit denen ich etwas anfangen kann.

Re: Fehlersuche

Verfasst: Donnerstag 22. Oktober 2015, 20:04
von pillmuncher
@NoPy: Zur Frage nach den Generatoren: tatsächlich betrifft das Iteratoren. Generatoren sind manche Dinge, die Iteratoren erzeugen. Der Zusammenhang ist dieser. Generatorfunktionen sind Funktionen oder Methoden, die wenigstens ein yield-Statement haben. Generatorausdrücke sehen aus wie List Comprehensions, außer dass sie in runde statt eckige Klammern eingeschlossen sind. Wenn ich eine Generatorfunktion ausführe, erhalte ich als Ergebnis einen Generator zurück. Wenn ich einen Generatorausdruck auswerte, ebenfalls. Generatoren implementieren das Iterator-Protokoll, dh., sie besitzen eine __iter__() und eine __next__() Methode (next() in Python2.x). Generatoren haben zusätzlich noch weitere Methoden: send() und close() und throw(). Objekte, die eine __iter__() Methode haben, implementieren das Iterable-Protokoll. Wir haben also eine Hierarchie: Iterable <-- Iterator <-- Generator. Das ist keine Vererbungshierarchie, denn wir arbeiten hier mit Duck-Typing, nicht mit Vererbung. Deswegen nennen wir es auch Protokoll, und nicht Basisklasse. Jedes Objekt kann jedes beliebige Protokoll implementieren, wenn der Programmierer das so festlegt (indem er es so programmiert).

Iteratoren haben also genau zwei Methoden: __iter__() und __next__(). Die erste ist dafür da, damit man auf einem Iterator it wieder iter(it) aufrufen kann. Das braucht man manchmal. it wird also von iter() als Iterable verwendet. Die zweite Methode dient zum eigentlichen iterieren. In einer for-Schleife wird sie implizit aufgerufen, aber das kann man auch explizit machen, indem man zB. elem = next(it) schreibt. Die Funktion next() könnte man, wenn es sie nicht schon gäbe, so implementieren:

Code: Alles auswählen

def next(iterator):
    return iterator.__next__()
Analog dazu ruft iter(it) einfach it.__iter__() auf, aber wenn das fehlschlägt, versucht iter() über den Subscriptionsoperator an die Elemente zu kommen. Das interessiert uns hier aber nicht.

Man verwendet Generatoren bevorzugt dann, wenn das Berechnen der Elemente sehr teuer ist oder die Elemente sehr groß sind. Oder wenn es sehr viele Elemente sind, so dass eine Liste mit allen Elementen sehr viel Speicher benötigen würde. Oder in einem Fall wie diesem: Nehmen wir an, ich wollte einen Iterator haben, der Daten aus einer Netzwerkverbindung liefert, also Daten, die irgendein anderer Computer sendet. Nehmen wir irgendein simples Protokoll an, wo nur Strings gesendet werden, und ein '\n' sei der Trenner zwischen zwei Strings und '\0' signalisiert das Ende der Daten. Nach senden von '\0' wird die Verbindung geschlossen. Ich könnte das so implementieren:

Code: Alles auswählen

class NetIter:

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def __iter__(self):
        connection = some_connect_function(self.host, self.port)
        while True:
            data = ''
            while True:
                data += connection.recv()
                if data[-1] == '\n':
                    yield data[:-1]
                    break
                elif data[-1] == '\0':
                    yield data[:-1]
                    return
Iter ist hier als Generatorfunktion implementiert. Angenommen, ich würde sowas machen:

Code: Alles auswählen

netit = NetIter('localhost', 12345)
xs = list(netit)
ys = list(netit)
Würdest du da erwarten, dass xs und ys dieselben Daten enthalten? Wenn nein, warum dann bei openpyxl? Dass die Daten über das Netzwerk kommen, ist dabei übrigens völlig unerheblich. Wichtig ist dagegen, was das Iterator-Protokoll garantiert: dass ein Element nach dem anderen geliefert wird, bis es entweder keine mehr gibt oder keine mehr nachgefragt werden.

Ein völlig anderes Problem ist, was ich mit den gelieferten Elementen anstelle. Sequentiell aus allen Elementen einer Struktur eine neue, evtl. nicht-isomorphe Struktur zu erzeugen, ist ein left fold. Den kann ich mittels Rekusion erzeugen, wie du das in Dump() getan hast, oder mittels einer for-Schleife (evtl. + Stack) oder mittels reduce(). Einfach ist es, wenn alle Elemente auf dieselbe Weise behandelt werden müssen. Schwieriger, wenn man einen Dispatch durchführen muss. Der Dispatch kann durch Konditionale (if-Statements) durchgeführt werden, aber zB. auch dadurch, dass ich mir Pythons Methoden-Dispatch zunutze mache. Guido nannte das AFAIR mal das Command Dispatch Pattern:

Code: Alles auswählen

class Foo:
    pass

class Bar:
    pass

class Dispatcher:

    def dispatch_Foo(self, elem):
        print 'Foo found!', elem

    def dispatch_Bar(self, elem):
        print 'Bar found!', elem

    def dispatch(self, elem):
        getattr(self, 'dispatch_' + type(elem).__name__)(elem)

d = Dispatcher()
d.dispatch(Foo())
d.dispatch(Bar())
Das hat aber dasselbe Problem wie explizite if-Abfragen und Visitors. Man muss immer, wenn ein neuer Fall dazukommt, alle Stellen ändern, wo man auf den neuen Fall reagieren muss. Aber das ist kein Problem von Python oder dynamisch typisierten Sprachen, sondern von allen mir bekannten Sprachen. Auch statische Typisierung hilft hier nicht, denn es gibt keine automatische Typprüfung der Art "Oh, hier ist was neues, da generiere ich mal automatisch den Code, den der Programmierer haben möchte, um mit dem neuen Dings umzugehen." Und auch mit Iteratoren hat das nichts zu tun, denn für diese Problemstellung ist es wurst, ob die Daten von einem Iterator geliefert werden oder aus einer Liste oder sonstwo her stammen. Entweder die Elemente haben dasselbe polymorphe Interface, wodurch man den Dispatch geschenkt bekommt, oder man muss den Dispatch selber durchführen, mit den og. Problemen. Manchmal muss man dazu Adapter (welche du Wrapper nennst) schreiben. Auch das hat weder etwas mit dynamischem vs. statischem Typing zu tun, noch mit Iteratoren.

Schlussendlich: mir ist immer noch nicht klar, wo dein Problem liegt. Abgesehen von dem oben gezeigten Command Dispatch Pattern, das so in Bondage & Discipline Sprachen nicht implementierbar ist, hat das alles nichts mit Python zu tun, sondern nur ganz allgemein mit OOP.

Re: Fehlersuche

Verfasst: Freitag 23. Oktober 2015, 07:18
von NoPy
pillmuncher hat geschrieben:Würdest du da erwarten, dass xs und ys dieselben Daten enthalten? Wenn nein, warum dann bei openpyxl? Dass die Daten über das Netzwerk kommen, ist dabei übrigens völlig unerheblich. Wichtig ist dagegen, was das Iterator-Protokoll garantiert: dass ein Element nach dem anderen geliefert wird, bis es entweder keine mehr gibt oder keine mehr nachgefragt werden.
Ganz ehrlich habe ich erwartet, dass mich die interne Implementation nicht interessieren muss. Wenn ich 2 mal hintereinander in ein Objekt hineinsehe, dann hätte ich gern 2 mal das gleiche Ergebnis. Ob dazu erneute eine Netzwerkverbindung etabliert werden muss oder was auch immer wäre mir egal...
pillmuncher hat geschrieben:Das hat aber dasselbe Problem wie explizite if-Abfragen und Visitors. Man muss immer, wenn ein neuer Fall dazukommt, alle Stellen ändern, wo man auf den neuen Fall reagieren muss. Aber das ist kein Problem von Python oder dynamisch typisierten Sprachen, sondern von allen mir bekannten Sprachen. Auch statische Typisierung hilft hier nicht, denn es gibt keine automatische Typprüfung der Art "Oh, hier ist was neues, da generiere ich mal automatisch den Code, den der Programmierer haben möchte, um mit dem neuen Dings umzugehen."
Schon klar, keine Ahnung, woher der Rechtfertigungsdruck kommt. Der einzige Unterschied zwischen statisch und dynamisch typisierten Sprachen an dieser Stelle ist der, dass der Compiler aufschreit, wenn der, dessen Klasse ich benutze, die Typen der Schnittstelle verändert hat. Wenn beispielsweise die Zellen nicht als Liste, sondern als Dictionary oder Generator ausgeliefert würden. Aber wie gesagt, ich will ja python lernen ...
pillmuncher hat geschrieben:Schlussendlich: mir ist immer noch nicht klar, wo dein Problem liegt. Abgesehen von dem oben gezeigten Command Dispatch Pattern, das so in Bondage & Discipline Sprachen nicht implementierbar ist, hat das alles nichts mit Python zu tun, sondern nur ganz allgemein mit OOP.
Das stimmt nicht ganz, wenn ich es darauf anlege, könnte ich beispielsweise auch Funktionen als Parameter benutzen und so den Patch quasi mitliefern. Aber auch das - ich brauch keinen Schwanzlängenvergleich zwischen python und irgend einer anderen Programmiersprache.

Wenn ich eine Frage der Form:
Wie mache ich etwas, das in BlaBlaBla so geht, mit python?

Dann fände ich folgende Arten von Antworten chic:
1. Das geht so:
2. Das macht man in python ganz anders, nämlich:
3. Das geht in python gar nicht. Lass Dir etwas anderes einfallen.

Das Infragestellen des Problems ist meist nervig und in der Regel wenig zielführend. Und ganz ehrlich PEP8 und PEP20 sind als Styleguide etwas dürftig.

Die Antwort von Dir war durchaus hilfreich, da ich ein Konstrukt von Python jetzt etwas besser verstanden habe (Command Dispatch Pattern)

Re: Fehlersuche

Verfasst: Freitag 23. Oktober 2015, 08:14
von Sirius3
@NoPy: um konkrete Antworten zu geben, muß man das Problem verstanden haben. Du hast angefangen irgendwelche abstrakten Probleme zu diskutieren, von Raumschiffen und Zimmern, wo die Antwort immer die selbe ist, dass man ein einheitliches Interface für alle Zimmer braucht. Python erzwingt halt nur nicht, wie andere OOP-Sprachen, einen gemeinsamen Vorfahren mit abstrakten Methoden. Dass es das nicht erzwingt, heißt aber nicht, dass man kein einheitliches Interface haben kann. Aber wie schon öfter geschrieben, mit großer Freiheit kommt auch die Verantwortung, selbst zu Denken und nicht alles den Compiler machen zu lassen.

Ganz ehrlich, Deine Erwartungen sind reichlich naiv. Würdest Du bei einem File-Handle erwarten, dass, wenn man die Datei einmal gelesen hat, das System automatisch wieder an den Anfang springt?

Wenn Du in Java refactoring dadurch machst, dass Du das Interface änderst und schaust, wo der Compiler überall schreit, dann hast Du dort schon etwas falsch gemacht. Wenn Du bisher ohne Tests ausgekommen bist und angeblich nur einmal im Jahr einen Fehler machst, dann passen Deine Programme auf einen PostIt-Zettel.

Niemand behauptet hier, Python wäre anderen Sprachen überlegen, aber da Du ja anscheinend Erfahrung mit anderen Programmiersprachen hast, versuchen wir hier Gemeinsamkeiten und Unterschiede zwischen den Sprachen herauszustellen. Von Machogehabe kann ich hier nichts finden.

Re: Fehlersuche

Verfasst: Freitag 23. Oktober 2015, 08:54
von NoPy
Sirius3 hat geschrieben:@NoPy: um konkrete Antworten zu geben, muß man das Problem verstanden haben. Du hast angefangen irgendwelche abstrakten Probleme zu diskutieren, von Raumschiffen und Zimmern, wo die Antwort immer die selbe ist, dass man ein einheitliches Interface für alle Zimmer braucht. Python erzwingt halt nur nicht, wie andere OOP-Sprachen, einen gemeinsamen Vorfahren mit abstrakten Methoden. Dass es das nicht erzwingt, heißt aber nicht, dass man kein einheitliches Interface haben kann. Aber wie schon öfter geschrieben, mit großer Freiheit kommt auch die Verantwortung, selbst zu Denken und nicht alles den Compiler machen zu lassen.
Natürlich, das ist mir ja nicht neu. Die Frage ist doch aber nicht, warum oder pauschal, wie. Es muss doch ein dazwischen geben wischen PEP8/PEP20 als megaabstrakt und jedem konkreten Sachverhalt. Ein abstraktes Beispiel, mit dem jeder aus der Anschauung heraus etwas anfangen kann als Beispiel hilft zumindest mir sehr. Und die darauf bezogenen Antworten waren für mich auch sehr hilfreich.
Sirius3 hat geschrieben:Ganz ehrlich, Deine Erwartungen sind reichlich naiv. Würdest Du bei einem File-Handle erwarten, dass, wenn man die Datei einmal gelesen hat, das System automatisch wieder an den Anfang springt?
Nein, natürlich nicht. Aber wenn ich das kapsle in eine Funktion/Klasse/was auch immer, die heißt "Dateiheader", dann würde ich schon erwarten, dass es sich nicht um ein EinmalLesen- Objekt handelt. Aber das steht doch schon lange nicht mehr zur Debatte, die Antwort habe ich doch schon am Anfang bekommen.
Sirius3 hat geschrieben:Wenn Du in Java refactoring dadurch machst, dass Du das Interface änderst und schaust, wo der Compiler überall schreit, dann hast Du dort schon etwas falsch gemacht. Wenn Du bisher ohne Tests ausgekommen bist und angeblich nur einmal im Jahr einen Fehler machst, dann passen Deine Programme auf einen PostIt-Zettel.
Was Du alles weißt...
Natürlich habe ich Tests gemacht und natürlich sind gelegentlich dabei auch kleinere Fehlerchen aufgetreten. Aber keine nennenswerten konzeptionellen, warum auch. In statisch typisierten Sprachen muss ich aber eben nicht auf Schnittstellen testen, ganz einfach. Das testet der Compiler halt für mich. Und natürlich nutze ich die Fähigkeiten meiner Werkzeuge aus, um schneller zu entwickeln. Ist nicht besser, nur anders.
Sirius3 hat geschrieben:Niemand behauptet hier, Python wäre anderen Sprachen überlegen, aber da Du ja anscheinend Erfahrung mit anderen Programmiersprachen hast, versuchen wir hier Gemeinsamkeiten und Unterschiede zwischen den Sprachen herauszustellen. Von Machogehabe kann ich hier nichts finden.
Ist nett gemeint, aber für mich nicht so direkt zielführend. Ich habe schon eine grundlegende Vorstellung von den Gemeinsamkeiten und Unterschieden. Was ich suche, sind praktikable Richtlinien zum richtigen Einstieg. Irgend etwas zwischen PEP* und wie mache ich eine Schleife von 1 bis 100. Und mir persönlich hilft das Raumschiffbeispiel sehr, kannst Du glauben.

Re: Fehlersuche

Verfasst: Freitag 23. Oktober 2015, 09:00
von pillmuncher
NoPy hat geschrieben:Ganz ehrlich habe ich erwartet, dass mich die interne Implementation nicht interessieren muss. Wenn ich 2 mal hintereinander in ein Objekt hineinsehe, dann hätte ich gern 2 mal das gleiche Ergebnis. Ob dazu erneute eine Netzwerkverbindung etabliert werden muss oder was auch immer wäre mir egal...
Du möchtest also eine Garantie in der Programmiersprache dafür, dass die Daten, die ein anderer Computer über das Netz schickt, jedesmal dieselben sind, wenn du zu ihm eine Verbindung aufbaust. Ok. Angenommen, die Daten stammen von einer Benutzereingabe, sagen wir aus einem Chat-Programm. Dann möchtest du also eine Garantie in der Programmiersprache dafür, dass ein anderer Benutzer immer dieselben Sachen eintippt, wenn dein Programm das so vorsieht. Ja, klar.

Was du außerdem übersiehst, ist dass das Ergebnis tatsächlich immer dasselbe ist. Du hast einen Iterator, also ein Objekt mit einem internen Zustand, und wenn der abgearbeitet ist, hast du immer noch denselben Iterator, nur mit einem anderen internen Zustand. Iteratoren funktionieren in allen mir bekannten Sprachen auf genau diese Weise. Wenn du unbedingt willst, dass Objekte keine internen veränderlichen Zustände haben, musst du in einer Sprache programmieren, die das sicherstellt. Haskell etwa.
NoPy hat geschrieben:ich brauch keinen Schwanzlängenvergleich zwischen python und irgend einer anderen Programmiersprache.

Wenn ich eine Frage der Form:
Wie mache ich etwas, das in BlaBlaBla so geht, mit python?

Dann fände ich folgende Arten von Antworten chic:
1. Das geht so:
2. Das macht man in python ganz anders, nämlich:
3. Das geht in python gar nicht. Lass Dir etwas anderes einfallen.
Wenn du ein konkretes Problem hast, kann man konkret helfen. Deine Problembeschreibungen sind aber viel zu abstrakt, um konkret beantwortet zu werden.

Achja: Schwanzlängenvergleich. Liest du eigentlich, was ich schreibe? Ich habe nirgends behauptet, Python sei viel superer als alle anderen Sprachen. Ich habe mich lediglich über Bondage & Discipline Sprachen lustig gemacht, aber auch das ist kein Schwanzlängenvergleich. Überall sonst habe ich darauf hingewiesen, dass die genannten Probleme alle genannten Sprachen betreffen. Das ist ein toller Schanzlängenvergleich, wo der Vergleicher sagt: "Hey, meine Sprache ist die superste von allen, weil sie genauso funktioniert, wie alle anderen."

Re: Fehlersuche

Verfasst: Freitag 23. Oktober 2015, 09:02
von Hyperion
NoPy hat geschrieben: Natürlich habe ich Tests gemacht und natürlich sind gelegentlich dabei auch kleinere Fehlerchen aufgetreten. Aber keine nennenswerten konzeptionellen, warum auch.
Öh... das kann ich nicht glauben, oder wir sind doch wieder bei extrem kleinen Programmen! Die Art der Typisierung hat gemein hin nichts mit fachlicher oder technischer Konzeption zu tun ;-) Insofern ist das bei allen Sprachen *gleich schwer* und vor allem gleich Fehler anfällig!