dynamisch Methoden erstellen mithilfe setattr

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
me11
User
Beiträge: 3
Registriert: Mittwoch 25. Juni 2008, 02:22

Hallo,

zwar gabs hier schon mehrere Fragen zu setattr, aber folgendes Problem konnte ich hier noch nicht finden. Sorry falls es doch schon gelöst wurde.

Also, ich möchte einem Objekt dynamisch Methoden mitgeben, und zwar mithilfe eines Arrays in dem die Namen der Methoden stehen und mit einer Schleife.
Etwa so:

Code: Alles auswählen

class Test:
    def __init__(self):
        for i in range(3):
            def f():
                print i
            setattr(self, 'method'+str(i),f)

instance = Test()
instance.method0()
instance.method1()
instance.method2()
Leider funktioniert das nicht, die Ausgaben lauten:
2
2
2
Was mache ich falsch? Irgendwie scheint er immer die letzte Definition von f() zu benutzen.



Ich habs auch mit probiert eval() pobiert, aber das

Code: Alles auswählen

class Test:
    def __init__(self):
        names = ["a","b","c"]
        for name in names:
            def eval(name)():
                print name
            setattr(self, 'method'+name,eval(name))

instance = Test()
instance.methoda()
instance.methodb()
instance.methodc()
geht leider auch nicht. :( Er scheint bei dem zweiten Ansatz zu denken, dass ich eval überschreiben will und sagt
invalid Syntax
dabei markiert er die leere Klammer in Zeile 5.



Gibts irgendeine Möglichkeit Methoden mithilfe eines Arrays und einer Schleife zu erstellen?



Vielen Dank.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Hallo me11

Funktioniern tut dein erster Ansatz schon, allerdings liegt das Problem dadrin das deine Funktion die du definiert sich die Variable i aus dem darüberliegenden Namespace holt und der ist nunmal am Ende des Schleifendurchlaufs 2.

Wie man an den Adressen sehen kann sind es 3 unterschiedliche Methoden.

Code: Alles auswählen

In [2]: instance.method0
Out[2]: <function f at 0x01288CB0>

In [3]: instance.method1
Out[3]: <function f at 0x01288B70>

In [4]: instance.method2
Out[4]: <function f at 0x01288BB0>
me11
User
Beiträge: 3
Registriert: Mittwoch 25. Juni 2008, 02:22

Hm, gut dann stellt sich die Frage wie ich die Funktion dazu bekomme den "aktuellen" Wert von i zu benutzen.

Hier sieht man nochmal was ich eigentlich vorhabe:

Code: Alles auswählen

class Editor:
    def __init__(self):
        self.IDCounter = 0
        names = ["Node",
                 "Selector",
                 "Action"] # imagine these were loaded through os.listdir
        for name in names:
            def f():
                item = getattr(__import__(name), name)(self.IDCounter)
                self.IDCounter += 1
                return item
            setattr(self, 'create'+name,f)
Ich möchte bestimmte (Unter-) Klassen instanzieren können indem sie einfach in einen Ordner geschoben werden, ohne im Editor etwas verändern zu müssen.
Ich muss die create-Methoden an ein Popup-Menü aus wxPython binden.
So in der Art:

Code: Alles auswählen

self.Bind(wx.EVT_MENU, eval("self.create"+names[i]), id=PopupIDs[i])
Daher wärs toll wenn jemand eine Idee hätte wie ich diese Methoden erstellen kann, ich komm leider nicht drauf. :(
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Folgender Trick funktioniert:

Code: Alles auswählen

for i in range(3):
  def f(i=i): return i
  setattr('method%d' % i, f)
Übrigens: Wenn f eine Methode sein soll, dann muss der erste Parameter das Objekt aufnehmen können. Er wird dann später automatisch gefüllt und das "i" darf erst der zweite Parameter sein.

Stefan
me11
User
Beiträge: 3
Registriert: Mittwoch 25. Juni 2008, 02:22

Jup danke, das i=i funktioniert tatsächlich! :D
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

der Trick bei der Sache ist ganz einfach der, dass i den aktuellen Wert von i als default-Wert bekommt.
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Oder man machts eben nicht mit lambda. lambda hat ein blödes scoping, das aber eigentlich auch manchmal ganz gut ist. Für sowas jedenfalls würd ich einfach ne Funktion definieren und gut ist.

Beispiel:
http://paste.pocoo.org/show/77808/

btw: Mein Lodgeit.py funzt auch net mehr :o

€dit:
scheint was mit meinen lokalen Modifikationen zu tun gehabt zu haben Oo
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Welches der Beispiele weiter oben hat denn "lambda" benutzt? Desweiteren: Was an dem Scoping ist denn blöd? Lambda hat - genau wie lokale Funktionen - die normale lexikografische Bindung, wie sie bereits für Algol 1960 erfunden wurde und sich in nahezu allen Sprachen wiederfindet.

Stefan
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Ok....mit lambda waren in dem Fall lokale Funktionen gemeint, tut mir leid für die Ungenauigkeit.

Es ging mit jedenfalls darum, dass das Scoping in dem Fall hinderlich ist, weshalb man die Funktion aus einem anderem Scope holen muss.

Code: Alles auswählen

def get_func(text):
    def inner():
        print text
    return inner

fs = []
gs = []
for i in xrange(20):
    def g():
        print i
    f = get_func(str(i))
    fs.append(f)
    gs.append(g)

map(apply, fs+gs)
Und mehr wollte ich damit nicht sagen.
Antworten