On Aug 27, 2015, at 08:22 PM, Max Brustkern wrote:
>> @@ -39,8 +40,7 @@
>> """Must return a backend when called with a single backend."""
>> class Backend(object):
>> pass
>> - _create_backend = lambda: Backend()
>> - backend = _pick_backend(dict(foo=_create_backend), '')
>> + backend = _pick_backend(dict(foo=Backend), '')
>
>I'm not quite a clever in python as everyone else here, so I just want to
>make sure that Backend is wanted here and not Backend() This seems to apply
>to most of the tests around this, so I imagine they'd be breaking if it were
>incorrect, I just like to be sure it's intentional.
The example given provides a great way to think about them.
lambda args: expression
is equivalent to
def <lambda>(args):
return expression
where the function is anonymous, i.e. it performs no name binding in the
current namespace. When you assign the `lambda: Backend()` to
_create_backend, it's the assignment that does the name binding to the
function object, so its exactly equivalent to:
def _create_backend():
return Backend()
Note too that when _pick_backend() is called, the code is passing in a
dictionary that maps the `foo` key to the *function object* created by the
lambda; specifically, the calling of the lambda-created function object is
deferred. It's exactly equivalent to just setting `foo` to the Backend
function object without calling it.
So in this case, the lambda is really just creating an unnecessary extra level
of call.
That's not quite the case where some of the lambdas take an argument, but you
can see how the translation to explicit function definitions should generally
go.
One thing to keep in mind about my translations. To be accurate, the
lambda-to-def conversion should return the value of the last expression, but I
don't do this in all cases because the tests appear to only care that an
exception is raised. Thus, the return values are mostly ignored. But for
correctness, you could return the values out of the locally defined
functions.
BTW, this equivalence is the reason why lambda are generally frowned upon.
They're almost never needed, and the savings in lines of code are usually not
worth the cost in readability or comprehension. It was probably the fans of
functional programming that tipped the balance against just removing them from
Python 3. ;)
>> === modified file 'setup.py'
>> --- setup.py 2015-08-19 00:25:00 +0000
>> +++ setup.py 2015-08-24 19:50:50 +0000
>> @@ -20,9 +20,16 @@
>> from setuptools import find_packages, setup, Extension
>>
>> import sys
>> +
>> +from setuptools import find_packages, setup, Extension
>
>It surprises me that these are needed and haven't been included
>previously. Was there something causing static analysis to not pick this up
>before?
Oh, that's weird. I don't remember adding that. Maybe it's a merge snafu?
P.S. you can play with this to see the equivalence in action:
On Aug 27, 2015, at 08:22 PM, Max Brustkern wrote:
>> @@ -39,8 +40,7 @@ dict(foo= _create_ backend) , '') dict(foo= Backend) , '')
>> """Must return a backend when called with a single backend."""
>> class Backend(object):
>> pass
>> - _create_backend = lambda: Backend()
>> - backend = _pick_backend(
>> + backend = _pick_backend(
>
>I'm not quite a clever in python as everyone else here, so I just want to
>make sure that Backend is wanted here and not Backend() This seems to apply
>to most of the tests around this, so I imagine they'd be breaking if it were
>incorrect, I just like to be sure it's intentional.
It is!
lambdas are defined here:
https:/ /docs.python. org/3/reference /expressions. html#lambda
The example given provides a great way to think about them.
lambda args: expression
is equivalent to
def <lambda>(args):
return expression
where the function is anonymous, i.e. it performs no name binding in the
current namespace. When you assign the `lambda: Backend()` to
_create_backend, it's the assignment that does the name binding to the
function object, so its exactly equivalent to:
def _create_backend():
return Backend()
Note too that when _pick_backend() is called, the code is passing in a
dictionary that maps the `foo` key to the *function object* created by the
lambda; specifically, the calling of the lambda-created function object is
deferred. It's exactly equivalent to just setting `foo` to the Backend
function object without calling it.
So in this case, the lambda is really just creating an unnecessary extra level
of call.
That's not quite the case where some of the lambdas take an argument, but you
can see how the translation to explicit function definitions should generally
go.
One thing to keep in mind about my translations. To be accurate, the
lambda-to-def conversion should return the value of the last expression, but I
don't do this in all cases because the tests appear to only care that an
exception is raised. Thus, the return values are mostly ignored. But for
correctness, you could return the values out of the locally defined
functions.
BTW, this equivalence is the reason why lambda are generally frowned upon.
They're almost never needed, and the savings in lines of code are usually not
worth the cost in readability or comprehension. It was probably the fans of
functional programming that tipped the balance against just removing them from
Python 3. ;)
>> === modified file 'setup.py'
>> --- setup.py 2015-08-19 00:25:00 +0000
>> +++ setup.py 2015-08-24 19:50:50 +0000
>> @@ -20,9 +20,16 @@
>> from setuptools import find_packages, setup, Extension
>>
>> import sys
>> +
>> +from setuptools import find_packages, setup, Extension
>
>It surprises me that these are needed and haven't been included
>previously. Was there something causing static analysis to not pick this up
>before?
Oh, that's weird. I don't remember adding that. Maybe it's a merge snafu?
P.S. you can play with this to see the equivalence in action:
def runme(func):
print(func())
def hey():
return 'hey'
yo = lambda: hey()
def sup():
return hey()
runme(hey)
runme(yo)
runme(sup)
% python3 foo.py
hey
hey
hey