简体   繁体   中英

What does the lambda self: do

This answer explains how to create test cases dynamically.

The answer's code:

class Tests(unittest.TestCase):
    def check(self, i, j):
        self.assertNotEquals(0, i-j)

for i in xrange(1, 4):
    for j in xrange(2, 6):
        def ch(i, j):
            return lambda self: self.check(i, j)
        setattr(Tests, "test_%r_%r" % (i, j), ch(i, j))

I've tested and it works, but I can't just figure out how ?

I have trouble understanding the lambda self: magic in play here, mainly:

  1. Is the lambda used here to perform the exact opposite of functools.partial() (ie to create a wrapper function with one extra parameter that is not yet known)
  2. Is self a meaningful keyword or would lambda spam would work just as well?
  3. What point is that lambda evaluated?
  4. How come the .check() is perfectly fine outside the class es scope?
  5. How do you do this without lambda? - if I understand correctly, you should be able to do without lambda (not to mention that I agree with Guido and Alex that it is an eyesore and I want to do without :)

First of all: The lambda is returned from a function to prevent that modified values of i and j will be used later.

Compare to the following:

for i in xrange(1, 4):
    for j in xrange(2, 6):
        setattr(Tests, "test_%r_%r" % (i, j), lambda self: self.check(i, j))

This will not work as expected, as all the lambdas will share the same namespace and will use the values of i and j after the end of the loop. If we use a separate function call, we introduce a new closure every time that captures the values of i and j at the point in time where the function is called.

We could achieve the same by saving the current values of i and j as default arguments of the lambda. For good measure, let's also use itertools.product to avoid the nested loop:

from itertools import product
from operator import methodcaller

for i, j in product(range(1, 4), range(2, 6)):
  setattr(Tests, "test_%r_%r" % (i, j),
          lambda self, i=i, j=j: self.check(i, j))

Is the lambda used here to perform the exact opposite of functools.partial() (ie to create a wrapper function with one extra parameter that is not yet known)

Not really. It just calls the check(i, j) method on whatever it is given as an argument. This is used here to dynamically add methods to the Tests class.

Is self a meaningful keyword or would lambda spam would work just as well?

spam would work just as well. They choose self here due to convention because the lambda represents a method.

What point is that lambda evaluated?

As soon as test_[i]_[j]() is called on an instance of Tests .

How come the .check() is perfectly fine outside the classes scope?

Because it's inside a lambda will only be called later with an instance of Tests as the self argument.

lambda returns a function. self is its argument, and i and j are enclosed inside of it

So, ch(i, j) is a function of i and j which returns a function of self .

self does not have any magical meaning here as nothing in Python - every variable and function are explicit except of some built-ins. But as soon as these methods are attached to class, self becomes their 1st argument.

This means

  • after the loop The Tests class gets multiple methods called check_i_j

  • each of them has values i and j enclosed in it

  • each of them has their 1st param called self which is good cause they are defined inside of class, they are instance methods, and thus their 1st parameter should be called self by convention

a_function = lambda x: do_something_with(x)

is equivalent to

def a_function(x):
    return do_something_with(x)

In your particular example, the functions created are instance methods. Thus for example you could write:

class Tests:
...
   test_1_2 = lambda self: self.check(1, 2)

which would be equivalent to:

class Tests:
...
   def test_1_2(self):
       return self.check(1, 2)

Now, since the identifiers are generated dynamically, hence use of setattr .


  1. Is the lambda used here to perform the exact opposite of functools.partial() (ie to create a wrapper function with one extra parameter that is not yet known)

No, not really. In fact partial could be used here as well.

test_1_2 = lambda self: self.check(1, 2)

would became

test_1_2 = partial(Tests.check, i=1, j=2)
  1. Is self a meaningful keyword or would lambda spam would work just as well?

Instance method's first argument always needs to be the instance. The convention is to name that parameter self , but you can use any other name. I would not advise it, as it will make your code harder to read and understand.

  1. What point is that lambda evaluated?

lambda creates anonymous function, which as any function is evaluated upon call.

  1. How come the .check() is perfectly fine outside the classes scope?

It isn't. It's self.check() , where self is an instance of Tests class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM