Andrew Schmitt
Andrew Schmitt

Reputation: 145

Lambda use case confusion

I've been playing around with Celery / Django. In their example celery.py file there is the following line

app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, force=True)

Where lambda:settings.INSTALLED_APPS is the actual parameter for the formal parameter packages in autodiscover_tasks(). And settings.INSTALLED_APPS is a tuple.

autodiscover_tasks() then either calls the function it was passed or directly assigns the variable it was given in one of its first lines...

packages = packages() if callable(packages) else packages

So my question is. I just don't get why this was done this way. It seems very redundant. Why not just pass settings.INSTALLED_APPS as the tuple god wanted it to be. Why pass an anonymous function that calls it instead? What am I missing here?

Upvotes: 9

Views: 384

Answers (2)

timgeb
timgeb

Reputation: 78650

Adding on to Daniel's answer, here's a rather minimal example to showcase "late evaluation" by passing a callable.

>>> def foo(arg):
...     return lambda: arg() if callable(arg) else arg

This is a function that returns another function in order to fake asynchronous execution.

Build two functions:

>>> foo_arg = 3
>>> foo1 = foo(foo_arg)
>>> foo2 = foo(lambda: foo_arg)

Change foo_arg after creation of foo1 and foo2:

>>> foo_arg = 4

Call the functions:

>>> foo1()
3
>>> foo2()
4

foo1 uses the old foo_arg it was constructed with, foo2 calls the anonymous function to get the current value of foo_arg.

Upvotes: 4

Netwave
Netwave

Reputation: 42678

Since Celery is asyncrhonous it is not fixed that settings.Installed_Apps will not change while performing other computations, so wrapping it inside a lambda encapsulates it value as a reference until it gets called.

EDIT (adding an example as commented):

setting.INSTALLED_APPS = 10
app.autodiscover_tasks(settings.INSTALLED_APPS, force=True) #is called with installed_apps = 10, so it give you an output.

now think of this, while app.autodiscover_tasksis being called and its internall computations are made some other thing is being computed, and setting.INSTALLED_APPS now = 8, since you did use the variable, your call is using 10 instead of '8', but encapsulating it into the lambda (app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, force=True)) it will get the value when he needs it, synchronizing with its actual value wich should be 8.

Upvotes: 9

Related Questions