eval() in schleifen

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
Leonard
User
Beiträge: 15
Registriert: Freitag 26. Dezember 2008, 15:01

Hallo
Ich habe ein Programm geschrieben, dass aus trigonometrische Funktionen, mit dem Modul PIL, ein Bild aus Spektralfarben erzeugt. Dazu muss für jeden einzelnen Pixel der Funktionswert errechnen werden, was im Prinzip kein Problem ist. Jedoch will ich gerne, dass man den Funktionsterm im Programm eingeben kann und genau das ist das Problem: die Funktion eval() ist einfach zu langsam. Bei einer Auflösung von 400 * 400 Pixeln muss eval() schon 160.000 mal verwendet werden. Gibt es eine Möglichkeit nur einmal eval() anzuwenden und dann damit weiter zu arbeiten, schließlich verändert sich der Funktionsterm nicht.

hier das Programm:

Code: Alles auswählen

# -*- coding: cp1252 -*-
from Tkinter import *
import Tix
from math    import *
from time    import *
from PIL import Image

class Funktionsplotter:
    def __init__(self,root):
        root.resizable(False,False)
        root.title("Funktionsplotter2.0")

        self.setTerm("cos(radians(sqrt(x**2+z**2)))")
        self.setXStart(-360)
        self.setXEnde(360)
        self.setZStart(-360)
        self.setZEnde(360)
        self.setBreite(400)
        self.setHoehe(400)
        
        self.__addWIDGETS()

        self.funktionsTerm.insert(0,self.getTerm())
        self.xStart.insert(0,self.getXStart())
        self.xEnde.insert(0,self.getXEnde())
        self.zStart.insert(0,self.getZStart())
        self.zEnde.insert(0,self.getZEnde())
        self.breite.insert(0,self.getBreite())
        self.hoehe.insert(0,self.getHoehe())
    #SET METHODEN
    def setTerm(self,pTerm):
        self.__zTerm=pTerm
    def setXStart(self,pXStart):
        self.__zXStart=int(pXStart)
    def setXEnde(self,pXEnde):
        if self.getXStart()<int(pXEnde):
            self.__zXEnde=int(pXEnde)
    def setZStart(self,pZStart):
        self.__zZStart=int(pZStart)
    def setZEnde(self,pZEnde):
        if self.getZStart()<int(pZEnde):
            self.__zZEnde=int(pZEnde)
    def setBreite(self,pBreite):
        self.__zBreite=int(pBreite)
    def setHoehe(self,pHoehe):
        self.__zHoehe=int(pHoehe)
    #GET METHODEN
    def getTerm(self):
        return self.__zTerm
    def getXStart(self):
        return self.__zXStart
    def getXEnde(self):
        return self.__zXEnde
    def getZStart(self):
        return self.__zZStart
    def getZEnde(self):
        return self.__zZEnde
    def getBreite(self):
        return self.__zBreite
    def getHoehe(self):
        return self.__zHoehe
    def getXIntervall(self):
        lTemp=float((self.getXEnde()-self.getXStart()))/float(self.getBreite())
        return lTemp
    def getZIntervall(self):
        lTemp=float((self.getZEnde()-self.getZStart()))/float(self.getHoehe())
        return lTemp
    
    def __addWIDGETS(self):
        distanz =2#padx,pady für frame
        distanz2=1#für restliche komponenten
        self.frame=Label(root)
        self.frame.grid(column=0,row=1,padx=2,sticky=N+E+S+W)
        #Labels + Frames
        funktionstermFrame=LabelFrame(self.frame,text=" Funktionsterm ")
        funktionstermFrame.grid(column=0,row=0,columnspan=2,padx=distanz,pady=distanz,sticky=N+E+S+W)
        label=Label(funktionstermFrame,text="f(x,z)=")
        label.grid(column=0,row=0,padx=distanz2,pady=distanz2)
        
        wertebereichFrame=LabelFrame(self.frame,text=" Wertebereich in cm ")
        wertebereichFrame.grid(column=0,row=1,padx=distanz,pady=distanz,sticky=N+E+S+W)
        label=Label(wertebereichFrame,text="  X:  von ")
        label.grid(column=0,row=0,padx=distanz2,pady=distanz2)
        label=Label(wertebereichFrame,text="  Z:  von ")
        label.grid(column=0,row=1,padx=distanz2,pady=distanz2)
        label=Label(wertebereichFrame,text="   bis   ")
        label.grid(column=2,row=0,padx=distanz2,pady=distanz2)
        label=Label(wertebereichFrame,text="   bis   ")
        label.grid(column=2,row=1,padx=distanz2,pady=distanz2)
        
        aufloesungFrame=LabelFrame(self.frame,text=" Auflösung in Pixeln ")
        aufloesungFrame.grid(column=1,row=1,padx=distanz,pady=distanz,sticky=N+E+S+W)
        label=Label(aufloesungFrame,text="  Breite: ")
        label.grid(column=0,row=0,padx=distanz2,pady=distanz2)
        label=Label(aufloesungFrame,text="  Höhe:   ")
        label.grid(column=0,row=1,padx=distanz2,pady=distanz2)
        #Entrys
        self.funktionsTerm=Entry(funktionstermFrame,width=41)
        self.funktionsTerm.grid(column=1,row=0,padx=distanz2,pady=distanz2)
        
        self.xStart=Entry(wertebereichFrame,width=7)
        self.xStart.grid(column=1,row=0,padx=distanz2,pady=distanz2)
        self.xEnde =Entry(wertebereichFrame,width=7)
        self.xEnde.grid(column=3,row=0,padx=distanz2,pady=distanz2)
        self.zStart=Entry(wertebereichFrame,width=7)
        self.zStart.grid(column=1,row=1,padx=distanz2,pady=distanz2)
        self.zEnde =Entry(wertebereichFrame,width=7)
        self.zEnde.grid(column=3,row=1,padx=distanz2,pady=distanz2)

        self.breite=Entry(aufloesungFrame,width=7)
        self.breite.grid(column=1,row=0,padx=distanz2,pady=distanz2)
        self.hoehe=Entry(aufloesungFrame,width=7)
        self.hoehe.grid(column=1,row=1,padx=distanz2,pady=distanz2)
        #Buttons
        self.exportButton=Button(self.frame,text="Exportieren",command=self.export)
        self.exportButton.grid(column=0,row=3,columnspan=2,padx=distanz2,pady=distanz2,sticky=N+E+S+W)
        
    def export(self):
        self.exportButton.config(state=DISABLED)
        self.ladeBalken=Tix.Meter(self.frame,relief=FLAT,width=304,height=5,bd=0,fillcolor="blue")
        self.ladeBalken.grid(column=0,row=4,columnspan=2,sticky=N+E+S+W)
        start=time()
        
        self.setTerm(self.funktionsTerm.get())
        self.setXStart(self.xStart.get())
        self.setXEnde(self.xEnde.get())
        self.setZStart(self.zStart.get())
        self.setZEnde(self.zEnde.get())
        self.setBreite(self.breite.get())
        self.setHoehe(self.hoehe.get())
        
        self.pic = Image.new("RGB", (self.getBreite(),self.getHoehe()))
        
        self.xz2rgb()

        self.pic.save("f(x,z).jpg", "JPEG")
        ende=time()
        self.ladeBalken.destroy()
        self.exportButton.config(state=NORMAL)
        
        print "Exportdauer:",round(ende-start,5),"[sec]\nfür",self.getBreite()*self.getHoehe(),"Pixel"
    def xz2rgb(self):
        for h in range(self.getBreite()):
            x=self.getXStart()+h*self.getXIntervall()
            prozent=(float(100)/self.getBreite()*h+1)/100
            self.ladeBalken.config(value=prozent)
            self.ladeBalken.update()
            for v in range(self.getHoehe()):
                z=self.getZStart()+v*self.getZIntervall()
                y=eval(self.getTerm())
                self.pic.putpixel((h,v),(self.getFarbe(y)))
    def getFarbe(self,y):
        i=0.00196
        x=int(y/i)
        
        if 255<x<=510:
            r=255
            g=510-x
            b=0
        elif 0<x<=255:
            r=x
            g=255
            b=0
        elif -255<x<=0:
            r=0
            g=255
            b=abs(x)
        elif -510<=x<=-255:
            r=0
            g=510+x
            b=255
        else:
            r=0
            g=0
            b=0
        return r,g,b          
root=Tix.Tk()
funktionsplotter=Funktionsplotter(root)
root.mainloop()
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Guck dir mal compile an.

btw: Aus welchem Buch/Tut lernst du eigentlich Python?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Lass die Sternchen Importe weg, beache PEP 8 und lass simple getter und setter weg und benutzt statt dessen properties sollte dies notwendig sein. Das dürfte die Chancen (brauchbare) Antworten zu bekommen drastisch steigen lassen.

Mach aus dem eingegebenen Code eine echte Funktion mit lambda, dass dürfte dass ganze etwas beschleunigen.
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

mach aus dem eval eine lokale Referenz, anstatt jedes mal die globale zu verwenden. Das sollte auch etwas an Speed reibringen. Beispiel:

Code: Alles auswählen

def test():
    ev = eval
    for i in range(10000):
        ev(irgendwas)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Persönlich finde ich, dass ``eval()`` ein großer Schmarrn ist. Ich würde warscheinlich mit Pyparsing oder ähnlichem einen Parser für Formeln schreiben, diese Formeln in einen ``_ast``-kompatiblen AST schreiben, und diesen dann mit ``compile()`` in ein Code-Pbjekt kompilieren. Somit kann der User nix kaputtmachen, selbst wenn er es wollte, und der User hat mehr Freiheit beim Eingeben der Formeln (``3x`` geht in Python ja so nicht, etc, etc).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Hallo Leonard!

Versuchs mal damit:

Code: Alles auswählen

>>> f = eval("lambda x: x**2")
>>> f(3)
9
Statt den String mit y=eval("x**2") direkt auszuwerten kannst du ja auch mit obigen code eine Funktion erzeugen. Diese wird dann genau so schnell ausgewertet wie jede normale andere Funktion auch. Damit brauchst du auch nur ein eval. Zudem könntest du sicherlich deinen code noch weiter beschleunigen wenn du dann sowas wie numpy.fromfunction benutzt um dein Bild zu erzeugen.

@leonidas: Das ist eine der Gründe warum ich eval liebe :D. Ich finde es ist eines der wertvollsten und wichtigsten Sprachelemente überhaupt.

MFG HerrHagen
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

:shock:

Äh, was spricht gegen das hier?

Code: Alles auswählen

>>> f = lambda x: x ** 2
>>> f(3)
9
Man braucht also gar kein eval().
@leonidas: Das ist eine der Gründe warum ich eval liebe Very Happy. Ich finde es ist eines der wertvollsten und wichtigsten Sprachelemente überhaupt.
eval() interpretiert nur einen String als Python Code! Das kannst du nur dann funktionierend und "sinnvoll" anwenden, wenn du diesen String auf umständliche Art und Weise generierst. Das ist fehleranfällig und resultiert in schwer zu durchschauendem Code, da du gewissermassen eine Programm in einem Programm baucst. Zusätzlich ist ein solcher Code 1. in kleinen Anwendungen einfahc un unnötig (wie von dir gezeigt) und 2. bei größeren Anwendungen absolut wartungsresistent, wodurch die Fehlersuche zur Gänsejagd wird, insbesondere dann, wenn man den String im Verlauf des Programmes immer mehr erweitert (oder ähnlich damit verfährt). Auch kann in einen solchen String von außen durch bloße Anwendung der Anwendung theoretisch Code eingefügt werden, wenn man Variablen zur Codegenerierung benutzt. Kurz, es wird sehr oft vor eval gewarnt ("eval is evil"), und ich kenne keinen einzigen Fall der akzeptablen Nutzung von eval() (aber ich habe schon einige grauenvolle Anwendungen gesehen, wennauch nicht in Python).
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

HerrHagen hat geschrieben:Ich finde es ist eines der wertvollsten und wichtigsten Sprachelemente überhaupt.
Eval ist total überflüssig, i.d.R. immer nur eine behelfslösung weil man zu faul ist eine brauchbare Lösung umzusetzen aber selbst dann sollte man die Eingabe wenigstens validieren.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Äh, was spricht gegen das hier?
So wie ich die Sache verstanden hab, ging es darum aus einer Benutzereingabe einen String mit der Funktion zu nehmen und diese dann zu plotten.
Ein einfaches lambda scheidet somit aus.
Kurz, es wird sehr oft vor eval gewarnt ("eval is evil")
Diese Warnungen kommen doch nur von neidischen C-Programmierern die ein eval bitter vermissen :wink:. Klar kann man mit eval viel Mist machen, aber das kann man mit jedem anderem Sprachelement auch (lambda, if, ...). Ich halte es für ein ganz elementares Sprachelement.
ich kenne keinen einzigen Fall der akzeptablen Nutzung von eval()
Da widerspreche ich. Der obige Fall ist doch schon ziemlich sinnvoll. Ich glaube kaum das irgendeine Syntaxparsing-Geschichte sehr viel übersichtlicher wäre.
Ich hab noch ein anderes Beispiel. Ich setzte eval zur Steuerung eines Mess-Moduls einer Industrie-Maschine ein. Maschine und Modul sind über Ethernet verbunden. Von der Maschine können Befehle an das Modul geschickt werden (starte Messvorgang,...). Statt eine Nummer o.Ä. für die entsprechende Funktion zu verschicken, sende ich einfache einen String mit dem Befehl. Dies hat folgende Vorteile:
  • - ich hab keine Redundanz mehr drin. Wenn ich bspw. einen Funktionsnamen ändere, brauch ich wirklich nur diesen einen Namen ändern und nicht zusätzlich das Parsing des Befehls. Das ganze wird so viiielll kürzer und übersichtlicher
  • - ich kann schnell mal was ausprobieren. Ich sende einfach ein größeres Paket an Befehlen und schau ob es funktioniert. Das ganze kann ich dann später ordentlich direkt implementieren
  • - ich kann schnell mal was debuggen. Ich sende einfach den Befehl, dass mir der Wert einer bestimmten Variable geschickt werden soll (dazu braucht man ja auch noch mal eval :wink:). Das ist wichtig, da das Modul nur einen IPC ohne Bildschirm hat.
Dabei muss ich noch erwähnen, dass das ganze natürlich nicht mit dem Internet oder überhaupt irgendwas anderem verbunden ist, so dass eine böse Person Schabernack mit gefährlichen Behlen treiben könnte.

MFG HerrHagen
lunar

Ich kenne deine Situation nicht, daher bin ich geneigt anzunehmen, dass du dir den Einsatz von eval gut überlegt hast, und mit bestem Wissen und Gewissen auf Alternativen wie xmlrpc und pyro verzichtet hast.

Dann allerdings wäre diese Situation einer der wenigen Sonderfälle, in denen eval oder Codegenerierung im Allgemeinen sinnvoll ist.

Die Tatsache, dass eval unter bestimmten Umständen sinnvoll oder akzeptabel ist, macht es aber noch lange nicht zu einem essentiellen Sprachmittel. Nichts für ungut, aber ein Python-Entwickler, der eval() für ein elementares Sprachelement hält, hat Python nicht kapiert.

Es gibt eine Sprache, bei der viele Entwickler eval() für ein elementares Sprachmittel halten: PHP. Wohin das geführt hat, ist ja bekannt ...

In dieser speziellen Situation halte ich ein eval für den ersten Entwurf für akzeptabel. Ein sauber entwickeltes Programm dagegen nutzt einen Parser und erzeugt daraus einen Ast. Das hat zwei Gründe: Zum einen gibt es so eine klare Spezifikation der erlaubten Sprache, und somit auch die Möglichkeit, gefährliche oder unsinnige Dinge im Interesse des Nutzers zu verbieten. Zum anderen kann man die Sprache so auf ihren Zweck zuschneiden. Ein Ausdruck der Art "2x" ist in Python ungültig, für die Eingabe mathematischer Formel dagegen ist es in höchstem Maße angenehm, wenn man nicht jede Multiplikation mit einem Operator hinterlegen muss.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Statt eine Nummer o.Ä. für die entsprechende Funktion zu verschicken, sende ich einfache einen String mit dem Befehl.
Also machst du grundsätzlich das hier:

Code: Alles auswählen

def do_that(string):
    eval(string)

do_that("first(arg); second()")
? Warum dann nicht gleich so:

Code: Alles auswählen

def do_that(function):
    function()

def function_we_need():
     first(arg)
     second()

do_that(function_we_need)
Zumindest hört sich dein Problem danach an.
BlackJack

@HerrHagen: Ich möchte mich lunar anschliessen: `eval()` ist kein elementares Sprachmittel in Python. Python bietet so viele Möglichkeiten der introspektion und dynamisch Objekte zu erzeugen, dass die gerechtfertigten Einsatzmöglichkeiten von `eval()` verschwindend gering sind. Gerechtfertig ist IMHO nur was mit der Sprache nicht machbar ist. Denn wenn man Quelltext generiert, ist das in der Regel ein Zeichen dafür, dass die Sprache an der Stelle nicht ausdrucksstark genug ist.

Zu Deinem Programm: Schau Dir an wie es das `cmd`-Modul macht. Wenn man das mit dem `shlex`-Modul verbindet um die Argumente zu parsen, kann man auch ohne `eval()` einen flexiblem Kommandointerpreter ohne viel Redundanz bauen.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Die Tatsache, dass eval unter bestimmten Umständen sinnvoll oder akzeptabel ist, macht es aber noch lange nicht zu einem essentiellen Sprachmittel. Nichts für ungut, aber ein Python-Entwickler, der eval() für ein elementares Sprachelement hält, hat Python nicht kapiert.
Ich verneine das und behaupte das Gegenteil. :wink:
Das hat zwei Gründe: Zum einen gibt es so eine klare Spezifikation der erlaubten Sprache, und somit auch die Möglichkeit, gefährliche oder unsinnige Dinge im Interesse des Nutzers zu verbieten.
Eine Spezifikation der Sprache findest du unter python.org. Einen Interpreter für der dort beschriebenen Sprache ebenfalls. Warum sollte man einen Parser bauen und das Ergebnis dann interpretieren wenn es so etwas schon gibt.
Deine Argumentation stützt sich also im wesentlichen darauf, dass ein Benutzer ja etwas böses eingeben könnte was dann ausgeführt wird. Dies lässt aber all die Fälle vor, in denen dies nicht möglich ist oder keine Rolle spielt. Z.b. für eine Anwendung wie ich sie erwähnt hatte oder so etwas wie IDLE. Natürlich könnte der Benutzer etwas eingeben womit Daten von der Platte löscht. Das kann er aber auch einfacher haben indem er das einfach über das UI des Betriebsystems macht... Es gibt ja auch noch Anwendungen für Python außerhalb von Websites.

@BlackJack: Ich halte eval/exec für so wichtig, weil so eine neue Ebene von Möglichkeiten entsteht. Erzeugung von Code zur Laufzeit mag nicht so oft gefragt sein. ABER wenn dies der Fall ist, dann stehen schnell 5 Zeilen eval-Code 5000 Zeilen geparse entgegen.
Ich kenne die beiden Module. Ich weiß allerdings nicht warum ich sie benutzen soll wenns auch einfacher geht.

MFG HerrHagen
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

@str1442: Die Befehle kommen von "außerhalb" über Ethernet. Liegen also erstmal in str-form vor und können daher nicht direkt ausgeführt werden. Also eher statt:

Code: Alles auswählen

def func1: ...
def func2: ...
...

funcs = {'1': func1, '2':func2}
funcs[incoming_str]()
Mach ich:

Code: Alles auswählen

def func1: ...
def func2: ...
...

exec(incoming_str)
mit den oben beschriebenen Vorteilen aber auch Einschränkungen.
lunar

HerrHagen hat geschrieben:
Das hat zwei Gründe: Zum einen gibt es so eine klare Spezifikation der erlaubten Sprache, und somit auch die Möglichkeit, gefährliche oder unsinnige Dinge im Interesse des Nutzers zu verbieten.
Eine Spezifikation der Sprache findest du unter python.org. Einen Interpreter für der dort beschriebenen Sprache ebenfalls.
Der OP möchte keinen Python-Interpreter, sondern einen Interpreter für eine mathematisch sinnvolle Eingabesprache. Das ist nun mal nicht das Gleiche.

Diese Aussage lässt sich im Übrigen auf nahezu jede Anwendung von eval() vereinfachen.
Warum sollte man einen Parser bauen und das Ergebnis dann interpretieren wenn es so etwas schon gibt.
So?

Code: Alles auswählen

>>> x = 4
>>> eval('2 * x')
8
>>> eval('2x')
------------------------------------------------------------
   File "<string>", line 1
     2x
      ^
SyntaxError: unexpected EOF while parsing
Dir sollte bekannt sein, dass "2x" eine gängige mathematische Schreibweise ist, aber kein gültiger Python-Code.

Ein vernünftiger Parser erlaubt eine auf mathematische Bedürfnisse zugeschnittene Syntax in Verbindung mit einer besseren Fehlerbehandlung und der Beschränkung des Nutzers auf das tatsächliche Sinnvolle.
Deine Argumentation stützt sich also im wesentlichen darauf, dass ein Benutzer ja etwas böses eingeben könnte was dann ausgeführt wird.
Dann hast du meine Argumentation nicht verstanden. eval() ist nicht deswegen schlecht, weil es Sicherheitslücken auftut. Das ist zwar in machen Anwendungen relevant, aber bei weitem nicht in allen.

eval() ist vor allem deswegen schlecht, weil es in der Regel zu mies geschriebenen, unwartbaren Code führt, der eine miese Fehlerbehandlung hat (wenn überhaupt), schlecht zu lesen ist, und meist nicht im geringsten dokumentiert oder spezifiziert. Dazu kommt noch, dass er in den meisten Fällen schlicht kaputt ist, weil schon an so einfachen Aufgaben wie der Maskierung von Zeichenketten scheitert. Es gibt Ausnahmen, aber die bestätigen ja im Allgemeinen nur die Regel.

Wenn du durch die PHP-Anspielung auf das Argument Sicherheit gekommen bist, so hast du diese Anspielung missverstanden bzw. nicht viel Erfahrung mit PHP.

In PHP wird eval für alles mögliche genutzt. Das fängt bei der dynamischen Erzeugung von Typen an und hört eigentlich nie auf. Ich habe schon Sortier- und Suchalgorithmen gesehen, die intern eval() genutzt haben. Das hat natürlich negative Auswirkungen auf die Sicherheit, aber schlimmer sind eigentlich die negativen Auswirkungen auf Les- und Wartbarkeit des Codes.
Erzeugung von Code zur Laufzeit mag nicht so oft gefragt sein. ABER wenn dies der Fall ist, dann stehen schnell 5 Zeilen eval-Code 5000 Zeilen geparse entgegen.
Nenne mir einen einzigen Fall, bei dem Code-Erzeugung zur Laufzeit gebraucht wird, und der nicht durch andere Sprachmittel auszudrücken ist.

Btw, ich bezweifele, dass der eval-Code in deinem Projekt wesentlich kürzer ist als gleichwertiges mit xmlrpc oder pyro.
BlackJack

@HerrHagen: Es geht nicht nur um "böse" Eingaben, sondern auch um Fehlerhafte, bei denen dem Benutzer unverständliche Fehlermeldungen um die Ohren gehauen werden, sofern er nicht gerade Python kann und nicht nur die Untermenge, die für einen Funktionenplotter interessant ist.

Eine Schnittstelle mit `cmd` und `shlex` wäre IMHO einfach sauberer als den kompletten Pythoninterpreter interaktiv zur Verfügung zu stellen. Was Du machst ist einfach nur Bequemlichkeit und nicht irgendwelche tollen Möglichkeiten nutzen, die ohne `eval()` nicht drin wären.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

@lunar: Wenn du dir mal das urspüngliche Programm anschaust, wirst du festellen, dass es darum ging schöne bunte Bilder von Funktionen zu erzeugen. Das man statt 2x nun 2*x schreiben muss, spielte dabei glaube ich nur eine untergeordnete Rolle.
Angenommen du willst unbedingt nun unbedingt ein Programm haben was 2x auswertet, musst du natürlich einen entsprechenden Parser schreiben.
Selbst dann gibt es aber noch eine Klasse Anwendung für eval. Du musst ja das geparste Stück Code irgendwie für alle 400x400Pixel ausführen. Das wäre normal verdammt langsam und ein einsatz von Fremdbibliotheken wie numpy wäre auf das codestück nicht so einfach möglich. Du könntest aber auch die Benutzereingabe erst parsen und daraus Python-Syntax erstellen. Damit lässt sich dann mit

Code: Alles auswählen

func = eval("lambda x: x**")
dynamisch eine Funktion erzeugen:
ACHTUNG nicht verwechseln mit:

Code: Alles auswählen

y = eval("x**")
Dann kannst du auch so etwas wie numpy.fromfunction oder die numerischen Integrationsmethoden von scipy und und und,.... nutzen ohne(!) irgendwelchen interface code zu erzeugen. Du hast ja eine ganz normale Python-Funktion erzeugt und nicht irgendeine Datenstruktur die nur dein Programm versteht. Ich hoffe das ist sinnvolle Anwendung genug. Ich warte auf Gegenbeispiele.

@BlackJack: Ich finde: weniger (einfacher) Code, weniger Fehler. Ich bin der Meinung bei Programmierung gehts ja grad um (gesunde) Bequemlichkeit. Ich hoffe dich überzeugt das kombinierte "Parsing dann eval" Beispiel von dem ich oben geschrieben hat.

MFG HerrHagen
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

HerrHagen hat geschrieben:Dann kannst du auch so etwas wie numpy.fromfunction oder die numerischen Integrationsmethoden von scipy und und und,.... nutzen ohne(!) irgendwelchen interface code zu erzeugen. Du hast ja eine ganz normale Python-Funktion erzeugt und nicht irgendeine Datenstruktur die nur dein Programm versteht. Ich hoffe das ist sinnvolle Anwendung genug. Ich warte auf Gegenbeispiele.
Mit ``_ast`` und ``compile()`` bekommst du genaus das gleiche hin und zusätzlich mit dem Vorteil dass du einen Parser für Mathematische Formeln (also etwa ``3(5+ln 2)`` was hinten und vorne kein gültiger Python Quelltext ist) hast der bei Fehler sinnvolle Fehlermeldungen ausgibt, statt zu versuchen das als Python-Code zu interpretieren und dem User bei Fehler irgendwelche für ihn unverständlichen Fehlermeldungen zu geben.
HerrHagen hat geschrieben:Ich finde: weniger (einfacher) Code, weniger Fehler. Ich bin der Meinung bei Programmierung gehts ja grad um (gesunde) Bequemlichkeit. Ich hoffe dich überzeugt das kombinierte "Parsing dann eval" Beispiel von dem ich oben geschrieben hat.
Bequemlichkeit ist das eine aber eine schlechte Lösung wiegt den geringen Arbeitsaufwand nicht auf. Ich kann zum Beispiel auch gar nichts machen, das ist sehr bequem und löst das Problem sogar noch schlechter.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Ich glaub du hast mich falsch verstanden. Mir ist schon klar, dass man (wie auch immer) parsen muss, wenn man eine nicht-python-syntax haben will. Mein vorschlag bezog sich darauf, wie man das Geparste dann in eine python-funktion umwandelt.
Das _ast Modul finde ich einfach fürchterlich kompliziert. Außerdem haben mich ein paar Dinge immer abgeschreckt es zu benutzen. Hier mal ein Ausschnitt aus der Dokumentation (habe unterstrichen was mich stört):
  • New in version 2.5.

    The _ast module helps Python applications to process trees of the Python abstract syntax grammar. The abstract syntax itself might change with each Python release; this module helps to find out programmatically what the current grammar looks like.
Zudem bin ich mir nicht sicher ob das ganze auch mit anderen Implementation von Python funktionieren würde.
Mit eval(lambda) kannst du eine Funktion zur Laufzeit nur mit reinen Python-Sprachmitteln und ohne Abhängigkeit von einem externen Modul erzeugen.
Ich glaube zudem das das Erzeugen eines Python-Strings weit weniger fehleranfällig ist, als das Modifizieren und Erzeugen eines AST-Baumes. Zumal dieser doch eher unübersichtlich ist. Bei einen Python-String sieht man auf einen Blick wenn man sich beim Erzeugen vertan hat. Nochmal: ich beziehe mich auf die Methode mit der man nach dem Parsen aus der damit erzeugten Datenstruktur z.B. eine Funktion erzeugt. Einen String wie "3(5+ln 2)" direkt z.B. durch irgendwelche Ersetzungen in "3*(5+ln(2))" umzuwandeln ist natürlich (zumindest in den umfangreicheren Fällen) Quatsch. Damit hast du natürlich recht.

MFG HerrHagen
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

HerrHagen hat geschrieben:
  • New in version 2.5.

    The _ast module helps Python applications to process trees of the Python abstract syntax grammar. The abstract syntax itself might change with each Python release; this module helps to find out programmatically what the current grammar looks like.
Zudem bin ich mir nicht sicher ob das ganze auch mit anderen Implementation von Python funktionieren würde.
Naja, wir können uns auch auf Python 2.2 limitieren, nur wozu? Python 2.5 hat sich nun in fast allen Bereichen durchgesetzt (außer Zope 2) und 2.6 tritt langsam aber stetig die Nachfolge an. Um es doch einmal besser zu formulieren: "Python 2.5 was released on September 19th 2006." Das ist 2 Jahre und 3 Monate her, 27 Monate also. Zeit genug um zu migrieren. Und wenn man nicht will, gibt es noch die lustigen PEAK-Libraries von PJE mit denen man auch verschiedene Dinge basteln kann (ich denke da an Sachen wie BytecodeAssembler etc).

birkenfeld hat ein kleines Beispiel was man mit ``_ast`` so machen kann. An einem Parser kommt man IMHO kaum vorbei, und dann hat man entweder die Möglichkeit selbst einen Interpreter zu schreiben oder eben einen AST zu generieren und es in Python-Code zu wandeln.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten