Python-Funktionszeiger


75

Ich habe einen Funktionsnamen in einer Variablen wie dieser gespeichert:

myvar = 'mypackage.mymodule.myfunction'

und ich möchte jetzt meine Funktion so nennen

myvar(parameter1, parameter2)

Was ist der einfachste Weg, um dies zu erreichen?


4
Warum nicht die Funktion selbst speichern? myvar = mypackage.mymodule.myfunctionist viel sauberer.
Ironfroggy

1
Aus einem Kommentar unten: «Es muss eine Zeichenfolge sein, da die Anwendung an der Stelle, an der sie definiert ist, die gewünschte Funktion nicht kennt, da es sich um eine generische App handelt.» - schneck
badp

Antworten:


112
funcdict = {
  'mypackage.mymodule.myfunction': mypackage.mymodule.myfunction,
    ....
}

funcdict[myvar](parameter1, parameter2)

1
Ich frage mich, was der Performance-Hit hier ist, wenn die Funktion sehr oft aufgerufen werden würde (innerhalb einer Schleife, Rekursion usw.).
Zanlok

5
Die Referenz ist bereits aufgelöst, sodass nur die Suche innerhalb des Diktats mehr Zeit benötigt, als eine lokale Funktion aufzurufen. Und selbst das kann gemildert werden, indem Sie nur einmal nachschlagen.
Ignacio Vazquez-Abrams

Was ist das "mypackage.mymodule.myfunction"? Wo kann ich mehr darüber lesen?
Orvar Korvar

@OrvarKorvar: Ersetzen Sie durch den Namen und die Referenz Ihrer Wahl.
Ignacio Vazquez-Abrams

Ah, jetzt verstehe ich. Fundict ist ein Wörterbuch.
Orvar Korvar

41

Es ist viel schöner, die Funktion selbst speichern zu können, da es sich um erstklassige Objekte in Python handelt.

import mypackage

myfunc = mypackage.mymodule.myfunction
myfunc(parameter1, parameter2)

Wenn Sie das Paket jedoch dynamisch importieren müssen, können Sie dies erreichen durch:

mypackage = __import__('mypackage')
mymodule = getattr(mypackage, 'mymodule')
myfunction = getattr(mymodule, 'myfunction')

myfunction(parameter1, parameter2)

Bedenken Sie jedoch, dass all diese Arbeiten für jeden Bereich gelten, in dem Sie sich gerade befinden. Wenn Sie sie nicht irgendwie beibehalten, können Sie sich nicht darauf verlassen, dass sie in der Nähe bleiben, wenn Sie den lokalen Bereich verlassen.


15
def f(a,b):
    return a+b

xx = 'f'
print eval('%s(%s,%s)'%(xx,2,3))

AUSGABE

 5

Ich werde ein Upvote geben, um dem Downvote entgegenzuwirken. Es ist möglicherweise nicht die beste Lösung. Ich denke, es ist eine hilfreiche Antwort, da es ein vollständiges, funktionierendes Beispiel zeigt.
Bryan Oakley

5
@zanlok Ja, das ist es! :-) Das wusste ich damals noch nicht.
Pratik Deoghare

@PratikDeoghare Ich bin Anfänger in Python. Könnten Sie erklären, was (xx = 'f') tut, danke.
RaHuL

9

Am einfachsten

eval(myvar)(parameter1, parameter2)

Sie haben keine Funktion "Zeiger". Sie haben eine Funktion "Name".

Während dies gut funktioniert, werden Sie eine große Anzahl von Leuten haben, die Ihnen sagen, dass es "unsicher" oder ein "Sicherheitsrisiko" ist.


1
"unsicher": Wenn myvar von Benutzereingaben stammt, ja :)
Federico A. Ramponi

1
@schneck, warum sollte es dann möglicherweise eine Zeichenfolge sein?
Mike Graham

2
@schneck: Wenn eval ('der String') nicht die richtige Funktion erzeugt, ist Ihre Frage unvollständig. Sie haben etwas Entscheidendes ausgelassen. Sie können versuchen, etwas zu veröffentlichen, das zusammen mit der detaillierten Fehlermeldung funktioniert, was nicht funktioniert.
S.Lott

2
@Derrick Turn, @Truptych: Sie hätten nur Recht, wenn die Zeichenfolge von einem böswilligen Soziopathen stammte. Benutzereingaben von nicht authentifizierten Personen im Internet betreffen wahrscheinlich böswillige Soziopathen. Fast alles andere betrifft im Allgemeinen keine böswilligen Soziopathen, wodurch das Sicherheitsrisiko auf genau das gleiche Risiko reduziert wird, wie wenn jemand den gesamten Quellcode für die Anwendung löscht.
S.Lott

6
@schneck, ich verstehe nicht, was du mit "da es eine generische App ist" hier meinen könnte. Wenn Sie dies als Zeichenfolgenliteral definiert haben, wissen Sie bereits genug darüber, dass Sie dies nicht tun müssen.
Mike Graham

6

Warum nicht die Funktion selbst speichern? myvar = mypackage.mymodule.myfunctionist viel sauberer.


4
modname, funcname = myvar.rsplit('.', 1)
getattr(sys.modules[modname], funcname)(parameter1, parameter2)

2

eval(compile(myvar,'<str>','eval'))(myargs)

compile (..., 'eval') erlaubt nur eine einzige Anweisung, so dass es nach einem Aufruf keine willkürlichen Befehle geben kann oder ein SyntaxError. Dann kann ein kleines bisschen Validierung den Ausdruck zumindest auf etwas beschränken, das in Ihrer Macht steht, z. B. das Testen auf "Mypackage", um zu beginnen.


2

Beim Erstellen einer Bibliothek für die Authentifizierung ist ein ähnliches Problem aufgetreten. Ich möchte, dass der App-Besitzer, der meine Bibliothek verwendet, einen Rückruf bei der Bibliothek registrieren kann, um die Autorisierung für LDAP-Gruppen zu überprüfen, in denen sich die authentifizierte Person befindet. Die Konfiguration wird als config.py-Datei übergeben, die importiert wird und ein Diktat enthält alle Konfigurationsparameter.

Ich habe das zum Laufen gebracht:

>>> class MyClass(object):
...     def target_func(self):
...         print "made it!"
...    
...     def __init__(self,config):
...         self.config = config
...         self.config['funcname'] = getattr(self,self.config['funcname'])
...         self.config['funcname']()
... 
>>> instance = MyClass({'funcname':'target_func'})
made it!

Gibt es eine pythonische Möglichkeit, dies zu tun?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.