Lambdafunktionen Eval oder Parsen?

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
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Ich lese eine csv-Tabelle aus und möchte nun eine Funktion

Code: Alles auswählen

select_rows
Ähnlich wie SQL

Funktionsaufrufe sollten wenn möglich

Code: Alles auswählen

select_rows("time > 10 and speed < 200")
sein. Wobei das bedeutet, dass der Wert der Spalte time größer 10 sein soll und gleichzeitig speed < 200.

Leider weiß ich nicht wie man das am besten implementier, vor allem, da aus der csv nur strings gelesen werden und ich manchmal Zahlen brauche
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Warum verarbeitest du deine Daten nicht einfach mit SQLite?
Das Leben ist wie ein Tennisball.
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Wüsste ich auch gerne...

Es soll sowohl eine Datenbankversion geben, als auch eine reine csv-Version.
Wenn es nach mir ginge würden sowieso alle Messwerte gleich in einer Datenbank angelegt, aber es geht nicht nach mir.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du kannst durchaus die Schnittstelle zu csv-Dateien beibehalten. SQLite bietet auch die Möglichkeit, eine Datenbank im Speicher anzulegen, ohne den Umweg über eine Datei zu gehen. Du musst dann nur noch deine Tabellen in SQL übersetzen. Schau dazu am besten in die Dokumentation des SQLite-Moduls und Suche nach ":memory:".
Das Leben ist wie ein Tennisball.
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Ich kenne das :memory:,

aber mir wurde klar gesagt, dass das hier nicht gewünscht ist, es gibt bisher ein skript dafür, welches ich ersetzen soll.

Code: Alles auswählen

select_rows -c  xxx  -o  [eq | lt |gt |neq | leq | get ] -v yyy   [-d] 
ist der bisherige Aufbau

Wobei

Code: Alles auswählen

-c   Spaltenname
-o Operation (lt = less than  ...
-v value Wert zum Vergleichen
-d -optional : sorgt dafür dass Stringvergleich statt float genommen wird
Ich könnte das ganze jetzt 1:1 nachbauen, bin aber der Meinung dass das schöner ginge und suche nun nach Möglichkeiten.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ok, wenn du Benutzereingaben verarbeiten musst, dann kommst du natürlich um das Parsen nicht herum. Dann hilft dir das ast-Modul weiter. Den gewonnen Syntax-Baum musst du nur noch prüfen, ob dieser lediglich die erlaubten Elemente enthält und anschließend kannst du ihn auswerten.
Das Leben ist wie ein Tennisball.
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

ich dachte zunächst an etwas einfacheres

Code: Alles auswählen

def sel_row(lrenaming = None , exprssion = None):
    varnames_str = expression.split(:)[1]
    varnames = [e.strip() for e in varnames_str.split(",")]

    lambda_func = eval("lambda " + expression)

    for line in file:
       # get values...
       values = ... 
       # evaluate lambda function with values
       result = lambda_func(*values)

Hat halt noch einige nachteile, jedoch einen kompletten syntaxbaum aufzusetzen? Das ist verdammt viel Arbeit

Edit:
Aber da ich ohnehin nur Ausdrücke wie

x < 3 or y>2

haben möchte, bin ich relativ sicher, dass es da schon was fertiges gibt
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hast du dir das ast-Modul überhaupt angeschaut? Dort wird der gesamte Baum für dich erzeugt und eine Auswertung ist geradezu lächerlich einfach. Deine Anforderungen übertriffst du damit sicher um Längen und die Implementierung sollte locker in einer Stunde erledigt sein.
Das Leben ist wie ein Tennisball.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Habe was vergleichbares gemacht um mit `ast` Funktionen mit Parameter und laufender Variable wie z.B. 2*x + p['a'] zu erkennen.
* Erklärung: https://bitbucket.org/ms4py/sitforc/dow ... parser.pdf
* Code: https://bitbucket.org/ms4py/sitforc/src ... cparser.py
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Ich bin leider hoffnungslos überfordert.

Ich weiß nicht mal , ob mein Ansatz richtig ist.

Bisher versuche ich, die Benutzereingabe mit Hilfe von compile in ein ast-Objekt zu bringen

Code: Alles auswählen

src = "time > 10 and mode = 'unset'"
ast_obj = compile(src, "<string>", "exec", ast.PyCF_ONLY_AST)
Ich erhalte sogar ein Ast-Objekt

Code: Alles auswählen

for a in ast.walk(ast_obj):
  print a
Liefert mir sogar halbwegs brauchbare Sachen, aber so richtig weiter weiß ich noch nicht.

Könnt ihr mir helfen? (Wie gesagt, ich hab KEINE ahnung, ob dasd was ich bisher getan hab auch nur halbwegs Sinn macht)
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Konntest mit dem 4 seitigen PDF etwa nichts anfangen? Dein Code sieht nicht so aus, als ob du das berücksichtigt hast...
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Ich muss sagen, das was du da geschrieben hast, ist schon "fortgeschrittener". Ich lerne grad mal Python seit nem Monat und da ist das doch recht hart.
Im Moment versuche ich, noch ein wenig mit ast herumzuspielen, um den Aufbau etwas genauer zu verstehen.

Ich werde etwas länger brauchen um das zu verstehen.
Es fängt mit

Code: Alles auswählen

ast.parse(..., mode="eval")
an (Ist es Zufall dass deine Funktionen alle eval heißen <es gibt eine Erklärung für eval im pdf, aber ob das hier schon zutrifft?

Die Methode install ist für mich ein wahres Rätsel.

Ich werde länger brauchen, um das zu verstehen. Deswegen noch die Spielereien.

Ich danke dennoch für die beiden Dokument, ich hoffe sie werden mir noch helfen

EDIT:
Sowet ich einsehen kann, fügst du den Typen von ast wie ast.Expression neue Methoden zu, welche du dann aufrufst. Aber wie gesagt, ich brauche Zeit.
Jetz seh ich mir mal den Unterschied ast.Expression und Module an
Zuletzt geändert von ravenheart am Dienstag 30. November 2010, 22:10, insgesamt 1-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Der ast.NodeVisitor nimmt dir die ganze Arbeit des Traversierens ab. Und wenn du mal die Attribute nicht kennst, dann hilft dir die "dir"-Funktion. Außerdem solltest du "ast.parse" verwenden statt "compile", dann sparst du dir auch die ganzen Parameter.
Das Leben ist wie ein Tennisball.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Ja, ich füge allen ast-Klassen die Methode `eval` hinzu, und kann die dann Bottom-Up aufrufen.
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Es wird langsam aber sicher peinlich...
Ich war grad in der Bibliothek und habe sämtlichste Pythonbücher durchforstet und nirgends etwas über ast gelesen ...

Also um mal eine Orientierung zu geben:

Ich versuche aus

Code: Alles auswählen

 "impl > 3 and mode == 'unset'"

Code: Alles auswählen

 " 3 > 3  and mode == 'unset'"
zu kreieren und dann auszuführen.

Mein Ansatz bisher:

Code: Alles auswählen

''' Diese Klasse ist nur zum Betrachten möglicher Zwischenschritte'''
class v(ast.NodeVisitor):
  def generic_visit(self,node):
    print type(node).__name__
    ast.NodeVisitor.generic_visit(self, node)
  def visit_Name(self, node):
    print "Name: ", node.id

''' Eigentliche Bearbeitung'''
class t(ast.NodeTransformer):
  ''' Wenn impl gefunden wurde, mache daraus einen Num-Knoten mit dem Wert 3 '''
  def visit_Name(self,node):
    if node.id =="impl":
      return ast.Num(3)
    else:
      return node
Ausführung:

Code: Alles auswählen

my = ast.parse("impl > 3 and mode =='unset'", mode="eval")
t().visit(my)
v().visit(my)

--->
Expression
BoolOp
And
Compare
Num
Gt
Num
Compare
Name:  mode
Eq
Str
Das sieht eigentlich ganz gut aus, dachte ich bisher.
Aber nun;

Code: Alles auswählen

c = compile(my, "<string>", mode="eval")

--->
required field "lineno" missing from expr
Ich kann die Fehlermeldung nicht deuten und bitte nochmals um Hilfe und Korrektur

Wahrscheinlich muss ich noch copy_location aufrufen


EDIT:

Code: Alles auswählen

class t(ast.NodeTransformer):
   ....:     def visit_Name(self,node):
   ....:         if type(d[node.id]).__name__ in ["int", "float"]:
   ....:             new_node = ast.Num(d[node.id])
   ....:             ast.copy_location(new_node, node)
   ....:             return new_node
   ....:         else:
   ....:             new_node = ast.Str(d[node.id])
   ....:             ast.copy_location(new_node, node)
   ....:             return new_node
   ....: 
scheint zu funktionieren, wobei d ein dictionary ist.
Aber wenn ich die Dokumentation richtig verstehe, sollte das, was ich vorhabe mir Subscript etwas leichter gehen.
Verbesserungen erwünscht
Zuletzt geändert von ravenheart am Mittwoch 1. Dezember 2010, 14:37, insgesamt 2-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Versuche doch mal "my.eval", so wie es auch in dem Beispiel von ms4py steht.

Du solltest dir angewöhnen, Klassen richtig zu bennen und vernünftig (mit vier Leerzeichen) einzurücken. Das ist besonders dann sinnvoll, wenn dir andere helfen sollen. Außerdem sind Strings keine Kommentare, benutze also letztere wenn du etwas erläuterst.
Das Leben ist wie ein Tennisball.
Antworten