[Python-de] Bei der Instantiierung einer Funktion Code ausführen

Albert Hermeling Albert.Hermeling at t-online.de
So Dez 9 10:03:11 CET 2012


Am 09.12.2012 02:55, schrieb Stefan Schwarzer:

Hallo Stefan,

> Hallo Albert,
>
> On 2012-12-08 22:01, Albert Hermeling wrote:
>> Ich habe eine Anwendung geschrieben die es Erlaubt Daten Strukturiert zu
>> speichern. Das für sich alleine ist überflüssig, dafür gibt es XML und
>> noch einiges Andere. Die Anwendung kann deshalb noch einiges mehr. Ich
>> möchte auf das "einiges mehr" nicht näher eingehen, das würde nicht zum
>> Thema passen. Nur soviel sei gesagt, es handelt sich um Textdateien die
>> von einem Parser verarbeitet werden. Die Daten selber werden in
>> verschachtelten Objekten der Klasse 'SectionXXX'  hierarchisch
>> gespeichert. Es werden schon eine Reihe von Datentypen unterstützt,
>> weitere sollen als Plug-in realisiert werden. Die Textdateien sind als
>> "Scripte" anzusehen, sie werden von Programmieren geschrieben, die diese
>> Bibliothek anwenden wollen. Sie sind somit genau so sicher oder unsicher
>> wie jedes andere Python Script auch sein kann.
>>
>> Meine Überlegung ist jetzt folgende, um Objeke der Klasse SectionXXX um
>> Methoden zu ergänzen, sollen diese als Funktions-Strings in der zugrunde
>> legenden Textdatei eingebettet werden. Dabei kann jede Sektion eigene
>> spezielle Funktionen haben. Man braucht also nicht eigene Klassen von
>> SectionXXX abzuleiten. Aus den Code Strings sollen per exec
>> Funktions-Objekte erzeugt werden, diese sollen per sec1.sub1.myMethod =
>> types.MethodType(myMethod, func) in ihre jeweilige Sektion eingefügt
>> werden. Da ich die Textdateien als sicher ansehe, könnte ich jetzt
>> einfach exec(string) aufrufen und das wer es gewesen. Aber wie ich nun
>> mal bin, ich fange an Nachzudenken und dabei kam mir halt dieser Gedanke.
> werden diese eingelesenen Funktionen nicht in deinem
> Programm ausgeführt? Falls ja, klingt es für mich nicht
> sinnvoll, Seiteneffekte beim _Definieren_ der Funktionen zu
> vermeiden, wenn nachher beim _Ausführen_ der Funktion
> sowieso alles Mögliche passieren kann.
Also ich möchte keine Seiteneffekte vermeiden, es ist Sache des 
Programmierers sich vorher Gedanken zu machen, was er mit den Daten in 
einer Sektion (und den Sub-Sektionen) tun möchte oder nicht. 
Schließlich, wenn der Programmierer eine "Normale Klasse" Foo schreibt 
und Methoden implementiert um die Daten zu bearbeiten, sollte er wissen 
was diese mit seinen Daten machen.  Aber trotzdem wird es ein paar 
Restriktionen geben. Zum Beispiel dürfen die Schlüsselwörter class 
global import nicht vorkommen. Class nicht, weil es dafür das Plug-in 
System geben wird. Import nicht weil diese zentral über eine 
Config-Datei gesteuert werden und weil ich es nicht mag Imports in einer 
Klasse zu **verstreuen**. Global nicht, weil das schlechter Still ist 
und ich nicht x globale Variablen überall stehen haben will.
> Was du beschreibst, macht zudem den Eindruck, als würdest du
> einen Teil der Funktionalität, die Python schon bietet, noch
> mal implementieren. So nett DSLs (domain-specific languages)
> auch scheinen mögen: Nach meiner Erfahrung handelt man sich
> mit deren Entwicklung und Einsatz oft einen relativ großen
> Aufwand (in Entwicklung und Fehlersuche) mit relativ wenig
> Zusatznutzen gegenüber "normalem" Python-Code ein.
Ich glaub jetzt muss ich mal ein kleines Beispiel bringen um das ganze 
etwas näher zu erläutern:

---Dateianfang---

DOCTYPE = "VERSION : 1.3.0, CHARSET : UTF8, ESC : !/" # Sprachversion 
und Kodierung;

[f # Normale Sektion f]
         in_f_1 = <"1" "2" "3">     # Das ist eine Python Liste;
         in_f_2 = ("10" "20" "30")  # Das ist ein Python Tuple;
         in_f_3 = {"100" "200"}    # Das ist ein Python Set
     [g # Normale Sektion g]
         in_g_1 = "Hallo Welt" # Ein String mit Leerzeichen ohne Umbruch;
         [SubS # Eine weitere Subsektion]
             namen = ("albert" "martin" "heinz");
             text = """Das hier ist Text der einen Zeilenumbruch
                           enthält. Der wird von drei ' oder " Umschlossen
                        """ # Leerzeichen links von " werden automatisch 
entfernt;
             hallo = "Hallo Erde ";
             printer = |printer"""
                                  def printer(self, multiplikator):
                                      print(self.hallo * multiplikator)
                             """ # Geplannt: Embedded Code;
             finePrinter = ||Printer"""((arg1, arg2), {kw1 = "foo", kw2 
= "bar})"""
                                  # Geplannt Plug-in;
         [/SubS]
     [/g]
     [h ("datatype : int") # Sektion h die nur Zahlen enthält]
         # Enthalten die Daten (rechts vom Gleichheitszeichen) keine
         # Leerzeichen können die Anführungszeichen weggelassen werden
         a = 1;
         b = 2;
         c = 3;
         e = 4;
         f = (1 2 3 4) # Container mit Zahlen sind erlaubt;
     [/h]
[/f]

---Dateiende---

Eingelesen wird die Datei so obj = poc_read("test.poc")

Auf die Attribute greift man so zu:

obj.f.in_f_1,

oder so

x = getattr(obj, "f.g.SubS.namen")

Attribute können so neu belegt werden:

obj.f.in_f_1 = "Neuer Wert"

aber auch so:

obj.f.newAttribute("Neuer Wert", "Mit Kommentar")

aber auch so:

setattr(obj, "f.g.SubS.name", ("foo", "bar", "egg"))

Sektionen werden so angelegt:

obj.f.newSection("Foobar", "Sektion Foobar", datatype = "txt")

Gespeichert wird die POC-Hierarchie  so:

poc_write(obj)

Erstellen kann man eine POC-Hierachie per Hand oder per poc_new die 
genaue Syntax erspare ich mir hier mal.

Was ist daran so Unpythonisch das ein Programmierer das nicht anwenden 
kann wenn er es denn will! ;-)
> Wenn die anderen Programmierer sich nicht mit Python
> auskennen, werden sie vermutlich lästige Fehler in ihrem
> Code einbauen. Wenn sie sich an dich wenden, darfst du unter
> Umständen diese Mischung aus DSL und dem anderen Code
> debuggen. Wenn die Programmierer dagegen erfahrener sind,
> werden sie voraussichtlich vorziehen, "ganz normales" Python
> zu schreiben, ohne sich Gedanken über abweichende
> Ausführungsmodelle machen zu müssen.
Also ich sehe POC als normales Python an. Man benützt diese 
POC-Hierarchie in weiten Teilen, so wie man eine ganz normale 
Objekt-Hierarchie benützen würde.
> Fazit: Ich würde eher versuchen, ein Design zu entwickeln,
> dass sich gut in die normale Entwicklung mit Python einfügt.
Meiner Meinung nach habe ich das gemacht. Aber na ja Geschmäcker sind 
verschieden; trotzdem hoffe ich, das ihr mein POC nicht in völlig in der 
Luft zerreißen werdet.:-)
>>> Die Funktion wird erst beim Aufruf ausgeführt. Kannst aber
>>> garantieren, dass
>>> nicht auch außerhalb der Funktion Anweisungen kommen? Zum Beispiel:
>>>
>>> s = """
>>> print('haha')
>>> def printer():
>>>        print("Böse Funktion: ", dir())
>>> """
>> Vieleicht so:
>>
>> def teste(s):
>>       l = s.strip().splitlines()
>>       func_line = l.pop(0)
>>       if not func_line.startswith("def ") and not
>> func_line.endswith("):\n"):
> Das schließt auch Funktionen aus, bei denen die
> Argumentliste nicht komplett in einer Zeile steht:
>
> def my_func(langer_bezeichner, noch_ein_langer_bezeichner,
>              und_noch_einer):
>      ...
>
> (Nebenbei: `splitlines` entfernt die Zeilenende-Zeichen;
> `\n` sollte also nicht Bestandteil des Strings sein.)
>
>>           raise SyntaxWarning("Will ich nicht haben")
>>       for item in l:
>>           if not item.startswith("    "):
>>               raise SyntaxWarning("Will ich nicht haben")
>>       return s
> Mir fällt zumindest eine "legale" Variante ein, bei der der
> Funktionsblock nur teilweise eingerückt ist:
>
> def my_func():
>      print """\
> Erste Zeile
> Zweite Zeile"""
>
> Das sieht natürlich nicht übersichtlich aus; daher ist es
> verständlich, wenn du "das nicht haben willst". ;-)
Das Stimmt, aber ich will mal nicht so Pingelig sein :-) . Bei alledem 
kann es mir nicht um ein sicheres exec gehen, das wird es unter Python 
nicht geben können.

Viele Grüße

Albert


Mehr Informationen über die Mailingliste python-de