Verbesserungsvorschläge und function(handle) Problem

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
ebassti
User
Beiträge: 9
Registriert: Mittwoch 10. Mai 2017, 22:49

Servus Zusammen,

ich würde gerne wissen, was man als "Profi" in Python an meinem Code ändern würde oder was auffällig schlecht implementiert ist.
Des Weiteren habe ich ein Problem mit dem "function handle" wie es in MATLAB heißen würde. Das Einlesen einer mathematischen Funktion, die abhängig von v1 und v2 ist, funktioniert nicht so, wie das gerne hätte. Es wird immer ein Startwert benötigt und direkt ausgewertet, was bei mir aber erst innerhalb eines Loops passieren soll. Mit dem lambda Befehl hab ich es auch nicht besser hinbekommen, weshalb ich mich jetzt an euch wenden muss :K

Es geht mir primär um den Aufruf der OptimiseWith... Funktion und dem funktionierenden Programmablauf nach Aufruf.

Code: Alles auswählen

import math as m
from sympy import *
import numpy as np
        
        
class TestFunction(object):

    def __init__(self, name, domain_size, discretization_step_size):
        self.name = name #string
        self.domain_size = domain_size #array of double [2]
        self.discretization_step_size = discretization_step_size # double
        print("object initialized")

    def __internal_function_evaluation__(self):
                # calculate something internally 
        P1=objective_function.design_variable1
        P2=objective_function.design_variable2

        L=4 
        qz=5
        EI=  10000
        EA= 100000
        h= 1.5*L
        L_2= L*L
        h_2= h*h
        L4=m.sqrt(L_2+h_2)
        L5=m.sqrt((2*L)**2+h**2)

        K=np.array([[(h/L4)**2*EA/L4+3*EI/L**3+12*EI/L**3 , 3*EI/L**2-6*EI/L**2,  -12*EI/L**3,                  -6*EI/L**2],
                    [3*EI/L**2-6*EI/L**2,                   3*EI/L+4*EI/L,         6*EI/L**2,                    2*EI/L],
                    [-12*EI/L**3,                           6*EI/L**2,    12*EI/L**3+3*EI/L**3+(h/L5)**2*EA/L5,  6*EI/L**2-3*EI/L**2],
                    [-6*EI/L**2,                            2*EI/L,             6*EI/L**2-3*EI/L**2,             4*EI/L+3*EI/L]])

        F=np.array([[5/8*qz*L+1/2*qz*L   -P1*h/L4],
                    [qz*L**2/8-qz*L**2/12],
                    [5/8*qz*L+1/2*qz*L   -P2*h/L5],
                    [qz*L**2/12-qz*L**2/8]])

        Kinv=np.linalg.inv(K)
        U=np.dot(Kinv,F)
        return U

        print("internal function called")
        pass

    def objective_function(self, design_variable1, design_variable2):
        
        print("external function called")
        intermediate_results = self.__internal_function_evaluation_()
                # do something with intermediate_results      
        v1= intermediate_results(0)
        v2= intermediate_results(2)
        
        v=test_function
        return v1, v2
        pass


class EvaluationStrategy(object):
    def __init__(self, test_function):
        self.test_function = test_function
        #self.test_function = (lambda v1, v2: test_function)(v1,v2)
        print(self.test_function)
                # save all evaluation data
                # save best data

    def general_strategy(self):
        print(self.test_function.domain_size)
        print(self.test_function.objective_function(5.0))


class OptimiseWithSteepestDescent(EvaluationStrategy):

        def Optimise_With_Steepest_Descent_SB(self):
                
                #F=self.test_function
                precision=0.0001
                gamma= 0.01
                cur_T1=0
                cur_T2=0
                prev_T1=cur_T1
                prev_T2=cur_T2
                previous_step_size1=100
                previous_step_size2=100
                
                

                while previous_step_size1 > precision or previous_step_size2 > precision:
                        prev_T1= cur_T1
                        prev_T2= cur_T2
                        df_T1= (TestFunction.objective_function(prev_T1,prev_T2)-TestFunction.objective_function(prev_T1+precision,prev_T2))/precision
                        df_T2= (TestFunction.objective_function(prev_T1,prev_T2)-TestFunction.objective_function(prev_T1,prev_T2+precision))/precision
                        cur_T1+= -gamma *df_T1
                        cur_T2+= -gamma *df_T2
                        prev_step_size1=m.sqrt((cur_T1)**2-(prev_T1)**2)
                        prev_step_size2=m.sqrt((cur_T2)**2-(prev_T2)**2)
                        
                print(cur_T1)
                return cur_T1, cur_T2
Zuletzt geändert von Anonymous am Samstag 27. Mai 2017, 21:07, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@ebassti: Ändern müsste man als Profi wahrscheinlich erst einmal das das funktioniert, denn da sind Fehler drin, das läuft ja nicht wirklich.

Es wird *alles* aus `sympy` importiert, aber *nichts* davon verwendet. Sternchenimporte sollte man sowieso nicht machen.

`TestFunction` ist sehr schräg. Das ist keine Klasse. Mit den Daten aus der `__init__()` wird nicht wirklich etwas sinnvolles gemacht, insbesondere nicht in den beiden ”Methoden” die eigentlich Funktionen sind, denn sie benutzen den Umstand das sie in einer Klasse definiert sind, überhaupt nicht.

`__internal_function_evaluation__()` hat vorne einen, und hinten zwei Unterstriche zu viel. Bei einem Aufruf hast Du dann auch noch einen der Unterstriche hinten vergessen. Es macht wenig Sinn so viel Code zu schreiben und den nie auszuprobieren. Programme werden entwickelt, und dabei auch immer getestet, ob das bisher geschriebene überhaupt das macht was es soll. Es macht wenig Sinn auf Klassen und Funktionen die nicht funktionieren, oder von denen man nicht weiss ob sie funktionieren, noch mehr Klassen und Funktionen zu setzen, bei denen man dass dann auch gar nicht weiss. Nur um am Ende dann festzustellen was alles nicht funktioniert, und was vielleicht sogar *so* *gar nicht* funktioniert, und komplett umgeschrieben werden muss.

`objective_function()` wird versucht auf der Klasse aufzurufen, was so nicht geht.

``pass`` macht relativ selten Sinn in Programmen und wenn dann nur in Zweigen in denen sonst *nichts* steht, denn nur dann braucht man es. Und auch Code *nach* einem ``return`` ist sinnlos, weil der niemals ausgeführt wird.

Bei den Klassen habe ich das Gefühl das Du Dich etwas übernommen hast, und vielleicht erst einmal einfachere Sachen probieren solltest als gleich irgendwelche grösseren Entwurfsmuster. Klassen mit nur einer `__init__()` und einer weiteren Methode, sind oft ein Zeichen für eine Funktion die zu einer Klasse aufgeblasen wurde. Und zu dem Problem das Du versucht hast mit ``lambda`` anzugehen, möchtest Du wahrscheinlich einen Blick auf `functools.partial()` werfen.

Die Vererbung zwischen `OptimiseWithSteepestDescent` und `EvaluationStrategy` macht mit dem gezeigten Code keinen Sinn, denn die Klassen arbeiten nicht zusammen, und ich sehe auch nicht warum wie das Eine eine Spezialisierung des anderen darstellt.
ebassti
User
Beiträge: 9
Registriert: Mittwoch 10. Mai 2017, 22:49

Danke erstmal für die schnelle und detaillierte Antwort!

Dass das ganze nicht funktioniert ist mir natürlich bewusst, wobei die Teile die ich zuvor entwickelt hatte funktioniert haben und ich dann aufgrund der vielen Verzweigungen nicht mehr wusste wie ich testen kann ohne "alles" zu haben.

sympy ist drin wegen einem Teil der später folgt, weil ich da keine andere Lösung gefunden habe außer über den Sternchenimport.

Da hast du Recht, die beiden Methoden machen da drinnen echt nicht viel Sinn, da muss ich mir noch überlegen, wie ich das unterbringe. Die Klasse ist jedoch für eine andere Unterklasse gedacht, um dann eben über einen Bereich alle Werte herauszugeben.

Mit Code nach einem Return, meinst du, dass in Optimise_With... die Outputs aus objective_function weiterbenutzt werden?

Mit dem Gefühl liegst du wahrscheinlich richtig ;) Problem daran ist, dass ich damit versuche meine Bachelorarbeit zu schreiben und es leider nicht anders lösen kann, es aber in der Form verlangt wird. Als Nicht-Informatiker mit neuer Programmiersprache usw. tut man sich doch relativ schwer ;)
BlackJack

@ebassti: Wie sind denn die vielen Verzweigungen zustandegekommen? Wenn man die erste neue Verzweigung hat die nicht funktioniert, dann schreibt man ja nicht weitere hinzu die man dann nicht testen kann weil die erste nicht-funktionierende das blockiert. Mir ist das ein bisschen zu viel nicht funktionierender Code dort. In solch eine Situation sollte man sich möglichst nicht hinein maneuvrieren. :-)

Was heisst keine andere Lösung ausser dem Sternchen-Import? Die Lösung ist entweder explizit importieren was man braucht, oder nur das Modul importieren und dann überall explizit über das Modul an die Objekte darin zu gelangen.

Die Erklärung zu der Klasse macht so immer noch keinen Sinn. Versuch den Code immer so einfach wie möglich zu halten. Wenn man keine Klasse braucht, sollte man auch keine schreiben, auch nicht auf Vorrat weil man das später (vielleicht) mal braucht, aber noch nicht weiss wie man das dann macht.

Mit Code nach ``return`` sind die Zeilen 43 und 44 und 56 gemeint. Mal von der Unsinnigkeit der ``pass``-Anweisungen abgesehen, wird auch das `print()` niemals ausgeführt.

In welcher Form wird das verlangt? Als sinnlose, unstrukturiete ”Klassen” mit ”Methoden” die gar keine sind? Was man als Funktionen viel einfacher und übersichtlicher haben könnte? Klassen sind kein Selbstzweck. Was sind denn die Kriterien vorgegeben wurden, die *diesen* Code zur Folge hatten?
ebassti
User
Beiträge: 9
Registriert: Mittwoch 10. Mai 2017, 22:49

Zustande gekommen ist das ganze durch meinen Betreuer, der das ganze objektorientiert und möglichst generisch haben will. Er hat mir so ungefähr gesagt was für Klassen ich haben werde und daraufhin habe ich diese geschrieben. Mein Problem war auch, dass ich nicht wirklich wusste wie ich vorher testen kann, ob alles passt. Mir ist klar dass es immer besser ist sich langsam vorzuarbeiten, doch mit den Klassen und vielen Zusammenhängen untereinander kam ich nicht ganz klar, wie du ja schon feststellen konntest ;)

Hier

Code: Alles auswählen

x = Symbol('x',real=True)
        l=self.a
        y = -EI*(qz*(((6*x**2)-(6*l*x)+(l**2))/(12*EI))+(4/l-6*x/(l**2))*U[0]+((2/l)-((6*x)/(l**2)))*U[1])
benutze ich den Code wofür ich * importiert habe. Ich habe mich dafür nicht sonderlich tief in sympy eingelesen und deshalb vielleicht dümmlich importiert, aber solange es funktioniert hat es mir gereicht.

Also einfach TestFunction raushauen und einzelne Funktionen machen?

Z.B. die Klasse Testfunction hat mein Betreuer so vorgeschlagen, ohne jetzt vorzuhaben da noch mehr init oder andere Methoden zu integrieren. Da ich mich erst in objektorientiertes schreiben eingelesen habe, bin ich davon ausgegangen, dass das so ganz gut geschrieben ist auch wenn man nie wirklich auf die Klasse zugreift.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@ebassti: Klassen zu schreiben, bedeutet nicht gleich, dass man was generisch macht und umgekehrt heißt das Weglassen von Klassen nicht, dass man ungenerisch programmiert hätte.

Was ist eigentlich Deine Aufgabe? Eine Optimieralgorithmus programmieren? Dafür gibt es schon was fertiges in numpy, das braucht man nicht selbst schreiben. Und wenn Du das selbst schreiben sollst, schau Dir das an, was es schon gibt.

Gerade wenn man mit Programmieren anfängt, sollte man mit seinem Code so lange spielen, bis man ihn wirklich verstanden hat. Nicht irgendwas schreiben und denken, später wird das schon irgendwie.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Na ja, wenn Du Dich mit Klassen noch nicht auskennst, dann wirst Du wohl mit Funktionen eher zum Ziel kommen. Wenn Du Dich aber auskennst, dann lassen sich Funktionen mit kleineren Änderungen immer noch in Methoden für eine Klasse wandeln.
BlackJack

@ebassti: Wenn man in den Methoden nie auf die Klasse zugreift, dann sind es aus objektorientierter Sicht keine Methoden, sondern einfach nur Funktionen die in einer Klasse stecken. Eine Klasse ohne Methoden ist dann aber auch keine Klasse aus objektorientierter Sicht. Also ist das insgesamt keine objektorientierte Programmierung (OOP). OOP ist eine bestimmte Art zusammengehörende Daten und Funktionen zu Objekten zusammen zu fassen, Zustand in solchen Objekten zu kapseln, und ein Programm als eine Sammlung von Objekten zu sehen, die über Nachrichten miteinander kommunizieren.

Wenn man also nicht diesem OOP-Paradigma folgt, kann man, auch wenn *alles* in ”Klassen” und ”Methoden” steckt, trotzdem Programme schreiben, die kein bisschen objektorientiert sind. Das macht aber keinen Sinn. Insbesondere wenn einen die Programmiersprache gar nicht dazu zwingt das zu tun. In Java muss man beispielsweise rein aus syntaktischen Gründen alles als Methoden in Klassen formulieren, auch wenn es eigentlich nur eine Funktion ist. Bei Python aber nicht.

Man kann das auch umdrehen: Um objektorientiert zu programmieren braucht man gar keine Klassen als Sprachkonstrukt. Es gibt Programmiersprachen die von Grund auf objektorientiert angelegt sind, die aber kein spezielles Konstrukt für Klassen haben, nur Objekte. Io wäre ein Beispiel dafür. JavaScript ist eine weitere Sprache die erst kürzlich ``class`` als Syntaxkonstrukt bekommen hat. Trotzdem konnte und wurde in der Vergangenheit viel objektorientiert damit programmiert.

Selbst in Programmiersprachen die keine spezielle Unterstützung für OOP haben, kann und wird OOP praktiziert.

Aber wie schon gesagt, Klassen sind kein Selbstzweck. Wenn man eine Klasse durch eine einfachere Funktion ersetzen kann, dann sollte man das auch tun. Zumal ja schon gesagt wurde, dass sich das nicht mit generischer Programmierung ausschliesst. Und auch nicht im Weg steht, sollte man während der Umsetzung feststellen, dass man tatsächlich eine sinnvolle Klasse aus den Funktionen und Daten erstellen kann.

Kommt Dein Betreuer eventuell von einer anderen Programmiersprache? Vielleicht denkt er einfach ”statischer”, in einer Sprache in der es beispielsweise kein `functools.partial()` gibt und wo Funktionen/Methoden selbst keine Werte sind. Denn in solchen Sprachen braucht man viel eher Klassen, sofern sie sich überhaupt vermeiden lassen.

In Python braucht man neben OOP Klassen auch für einfache Wertverbundtypen, die mehrere Werte zu einem Objekt zusammenfassen *und* wo die Werte austauschbar sein müssen. Falls sie unveränderbar sind, kann man `collections.namedtuple()` verwenden um solche Datentypen zu erstellen.

Auch das kann eventuell zu OOP führen, wenn man feststellt, das es Sinn macht Funktionen die auf diesen Objekten operieren, näher an den Datentyp zu binden — als Methoden. Manchmal möchte man auch Operatoren überladen.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ich kann nicht immer alles nachvollziehen, was vorgestellte Scripts eigentlich machen sollen. In den ersten Tagen hier im Forum konnte ich es teilweise nicht einmal ansatzweise. Nur bevor einer beginnt mit Klassen zu experimentieren, sollte er sich schon mit Funktionen gut auskennen. Und in jedem Buch oder Tutorial für Einsteiger steht doch nun einmal unter Funktionen, dass nach der ersten zutreffenden return-Anweisung kein weiterer Code in einer Funktion mehr ausgeführt wird. Und so lange Funktionen nicht sitzen, würde ich mich eher mit diesen beschäftigen, bevor ich mit Klassen beginnen würde.
Antworten