Python: Lambda or Partial?

Lambda or Partial? Is there a preferred solution to pass an argument to a callback?

Using partial:

#partial

from functools import partial
import maya.cmds as mc


class ButtonWin(object):
    def __init__(self):
        self.win = mc.window()
        self.layout = mc.columnLayout(parent=self.win)
        for numberX in range(10):
            mc.button(label="Click Here %d" % numberX, parent=self.layout, command=partial(self.report, numberX)) #PARTIAL
            mc.showWindow()

    def report(self, buttonIndex, value):
        print "button %d got %s" % (buttonIndex, value)

f = ButtonWin()

Or Lambda:

#Lambda

import maya.cmds as mc


class ButtonWin(object):
    def __init__(self):
        self.win = mc.window()
        self.layout = mc.columnLayout(parent=self.win)
        for numberX in range(10):
            mc.button(label="Click Here %d" % numberX, parent=self.layout, command=lambda x: self.report(numberX, "True")) #LAMBDA
            mc.showWindow()

    @staticmethod
    def report(buttonIndex, value):
        print "button %d got %s" % (buttonIndex, value)

f = ButtonWin()

Thanks in advance!

I always use partial, though I know some people find lambdas easier to read and use them. For the most part they will behave exactly the same and whichever you choose will be personal preference.

For the most part…
If you are creating callbacks in a loop, and using a loop variable as an argument for the callback, then you should use partial. lambda evaluates the variable at runtime, whereas partial evaluates when the partial function is created. An example:

def printValue(v):
    print v
    
def lambdas():    
    callbacks = []
    for i in range(5):
        callbacks.append(lambda: printValue(i))
        
    for cb in callbacks:
        cb()
        
def partials():
    from functools import partial
    callbacks = []
    for i in range(5):
        callbacks.append(partial(printValue, i))
        
    for cb in callbacks:
        cb()
        
print 'lambdas:'
lambdas()
print 'partials:'
partials()

# 
lambdas:
4
4
4
4
4
partials:
0
1
2
3
4

This isn’t just an issue in for loops. If you change the value of a variable that is referenced in a lambda function you will get an unexpected result since it will resolve that variable whenever the lambda is called, not when it was created.

I should add that you actually can get lambdas to resolve variables at creation by passing them as default arguments. I just find it more unsightly (and don’t know if there are any gotchas or side-effects with this method).

callbacks = []
for i in range(5):
    callbacks.append(lambda x=i: printValue(x))

for cb in callbacks:
   cb()

0
1
2
3
4

In a simple context (just making a single callback or a series of one-off calls) lambdas are fine. If you’re re-using variables - which includes any form of looping (!!) partials are better because they don’t suffer from late resolution the way lamdas do.

Details here:

Thanks a lot for the info capper and Theodox! That’s really helpful.

Cheers!