Funktionen mit Argumenten an eine andere Funktion in Python übergeben?

203

Ist es möglich, Funktionen mit Argumenten an eine andere Funktion in Python zu übergeben?

Sagen Sie für etwas wie:

def perform(function):
    return function()

Die zu übergebenden Funktionen haben jedoch Argumente wie:

action1()
action2(p)
action3(p,r)
Joan Venge
quelle

Antworten:

289

Meinst du das?

def perform( fun, *args ):
    fun( *args )

def action1( args ):
    something

def action2( args ):
    something

perform( action1 )
perform( action2, p )
perform( action3, p, r )
S.Lott
quelle
10
Was ist mit benannten Parametern? Das heißt, def action1(arg1, arg2=None, arg3=None)wie können Sie beispielsweise ein Argument übergeben, das Sie arg3 zuweisen möchten?
ChaimKut
6
durchführen (Spaß, ** Argumente), siehe stackoverflow.com/questions/8954746/…
Mannaggia
Was passiert , wenn performund action1, action2auf verschiedene Dateien? @ S.Lott
Alper
@alper importiere sie
pfabri
122

Dafür ist Lambda da:

def Perform(f):
    f()

Perform(lambda: Action1())
Perform(lambda: Action2(p))
Perform(lambda: Action3(p, r))
Dave
quelle
7
Können Sie mir bitte auch aus Neugier sagen, warum Lambdas für diesen Fall nicht gut sind?
Joan Venge
11
Lambdas sind eines der besten Merkmale guter Programmiersprachen. Leider ist die Implementierung von Python stark eingeschränkt. in diesem Fall passen sie jedoch perfekt
Javier
3
Ich finde, dass die eingeschränkte Syntax fast undurchsichtig ist; Sie sind n00bz schwer zu erklären. Ja, sie funktionieren hier und die verwirrenden Merkmale der Syntax fehlen. Dies ist - vielleicht - das einzige Beispiel, das ich von einem Lambda gesehen habe, das nicht dunkel ist.
S.Lott
11
Damit Sie das Ergebnis der übergebenen Funktion abrufen können, wäre es nicht besser, wenn Perform () "return f ()" aufruft, als nur f () aufzurufen.
Mhawke
Ich denke, dass die Lambda-Version ziemlich ordentlich ist, aber seltsamerweise war es in Tests, die ich durchgeführt habe, langsamer, Funktionen über das Lambda aufzurufen als mit der in einer anderen Antwort diskutierten Methode fn (* args).
Richard Shepherd
39

Sie können die Teilfunktion von functools wie folgt verwenden.

from functools import partial

def perform(f):
    f()

perform(Action1)
perform(partial(Action2, p))
perform(partial(Action3, p, r))

Funktioniert auch mit Schlüsselwörtern

perform(partial(Action4, param1=p))
Null
quelle
1
functools.partialist auch vielseitiger, wenn performweitere Parameter an übergeben werden müssen f. Zum Beispiel könnte man anrufen perform(partial(Action3, p))und perform(f)so etwas tun f("this is parameter r").
Robert
13

Verwenden Sie functools.partial, keine Lambdas! Und ofc Perform ist eine nutzlose Funktion, Sie können Funktionen direkt weitergeben.

for func in [Action1, partial(Action2, p), partial(Action3, p, r)]:
  func()

Jochen Ritzel
quelle
3
Dies hängt davon ab, ob die Argumente an der Aufrufstelle von Perform ausgewertet werden sollen oder nicht.
Dave
6

(Monate später) ein winziges reales Beispiel, bei dem Lambda nützlich ist, teilweise nicht:
Angenommen, Sie möchten verschiedene eindimensionale Querschnitte durch eine zweidimensionale Funktion, wie Scheiben durch eine Reihe von Hügeln.
quadf( x, f )nimmt eine 1-d fund nennt es für verschiedene x.
Um es für vertikale Schnitte bei y = -1 0 1 und horizontale Schnitte bei x = -1 0 1 zu nennen,

fx1 = quadf( x, lambda x: f( x, 1 ))
fx0 = quadf( x, lambda x: f( x, 0 ))
fx_1 = quadf( x, lambda x: f( x, -1 ))
fxy = parabola( y, fx_1, fx0, fx1 )

f_1y = quadf( y, lambda y: f( -1, y ))
f0y = quadf( y, lambda y: f( 0, y ))
f1y = quadf( y, lambda y: f( 1, y ))
fyx = parabola( x, f_1y, f0y, f1y )

Soweit ich weiß, partialkann ich das nicht -

quadf( y, partial( f, x=1 ))
TypeError: f() got multiple values for keyword argument 'x'

(Wie füge ich Tags numpy, partiell, lambda hinzu?)

denis
quelle
5

Dies wird als Teilfunktion bezeichnet und es gibt mindestens drei Möglichkeiten, dies zu tun. Meine Lieblingsmethode ist die Verwendung von Lambda, da es die Abhängigkeit von zusätzlichen Paketen vermeidet und am wenigsten ausführlich ist. Angenommen, Sie haben eine Funktion add(x, y)und möchten add(3, y)als Parameter an eine andere Funktion übergeben, sodass die andere Funktion den Wert für bestimmt y.

Verwenden Sie Lambda

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = lambda y: add(3, y)
    result = runOp(f, 1) # is 4

Erstellen Sie Ihren eigenen Wrapper

Hier müssen Sie eine Funktion erstellen, die die Teilfunktion zurückgibt. Dies ist offensichtlich viel ausführlicher.

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# declare partial function
def addPartial(x):
    def _wrapper(y):
        return add(x, y)
    return _wrapper

# run example
def main():
    f = addPartial(3)
    result = runOp(f, 1) # is 4

Verwenden Sie teilweise von functools

Dies ist fast identisch mit dem lambdaoben gezeigten. Warum brauchen wir das dann? Es gibt nur wenige Gründe . Kurz gesagt, partialkann in einigen Fällen etwas schneller sein (siehe Implementierung ) und Sie können es für die frühe Bindung im Vergleich zur späten Bindung von Lambda verwenden.

from functools import partial

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = partial(add, 3)
    result = runOp(f, 1) # is 4
Shital Shah
quelle
1

Hier ist eine Möglichkeit, dies mit einem Verschluss zu tun:

    def generate_add_mult_func(func):
        def function_generator(x):
            return reduce(func,range(1,x))
        return function_generator

    def add(x,y):
        return x+y

    def mult(x,y):
        return x*y

    adding=generate_add_mult_func(add)
    multiplying=generate_add_mult_func(mult)

    print adding(10)
    print multiplying(10)
Stefan Grünwald
quelle
In jedem Fall muss man mehr tun, als nur eine Funktion an einen anderen zu übergeben. Ein Abschluss ist der richtige Weg.
Jake77