8.5. Functional Lambda¶
Lambda - Anonymous functions
When function is used once
When function is short
You don't need to name it (therefore anonymous)
- lambda¶
Anonymous function
>>> lambda x: x+1
<function <lambda> at 0x...>
Lambda Expressions:
>>> a = lambda x: x+1
>>> b = lambda x,y: x+y
Equivalent functions:
>>> def a(x):
... return x+1
>>> def b(x,y):
... return x+y
8.5.1. Syntax¶
lambda <arguments>: <expression>
8.5.2. Convention¶
Usually parameters are named
x
andy
Use shortest code possible
Do not assign
lambda
to variableLambda is anonymous function and it should stay anonymous. Do not name it
PEP 8 -- Style Guide for Python Code: "Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier". Lambda is anonymous function and it should stay anonymous. Do not name it.
Usually there are no spaces in lambda expressions (to make code shorter)
Bad:
>>> square = lambda x: x**2
>>> square(4)
16
Good:
>>> def square(x):
... return x**2
...
>>> square(4)
16
PEP 8 -- Style Guide for Python Code: "Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier":
8.5.3. Note to Programmers of Different Languages¶
query = 'SELECT * FROM users'
result = database(query).stream()
.filter(user -> user.age > 5)
.filter(user -> user.firstname == 'Mark')
.filter((x,y) -> x + y)
.collect(Collectors.toList());
8.5.4. Noop¶
>>> noop = lambda: ...
>>> def request(on_error = lambda: ...):
... ...
8.5.5. Lambda with Map¶
Increment:
>>> data = [1, 2, 3, 4]
>>>
>>> result = map(lambda x: x+1, data)
>>> list(result)
[2, 3, 4, 5]
8.5.6. Lambda with Filter¶
Even numbers:
>>> DATA = [1, 2, 3, 4]
>>>
>>> result = filter(lambda x: x%2==0, DATA)
>>> list(result)
[2, 4]
8.5.7. Performance¶
>>> %%timeit -r 1000 -n 10_000
... def increment(x):
... return x + 1
... map(increment, range(0,100))
271 ns ± 30.6 ns per loop (mean ± std. dev. of 1000 runs, 10000 loops each)
>>>
>>>
>>> %%timeit -r 1000 -n 10_000
... map(lambda x: x+1, range(0,100))
262 ns ± 29 ns per loop (mean ± std. dev. of 1000 runs, 10000 loops each)
>>> %%timeit -r 1000 -n 1000
... def increment(x):
... return x + 1
... list(map(increment, range(0,100)))
7.48 µs ± 608 ns per loop (mean ± std. dev. of 1000 runs, 1000 loops each)
>>>
>>>
>>> %%timeit -r 1000 -n 1000
... list(map(lambda x: x+1, range(0,100)))
7.36 µs ± 545 ns per loop (mean ± std. dev. of 1000 runs, 1000 loops each)
8.5.8. Use Case - 0x01¶
>>> data = [1, 2, 3, 4]
>>>
>>> result = map(lambda x: x**2, data)
>>> list(result)
[1, 4, 9, 16]
8.5.9. Use Case - 0x02¶
>>> PL = {'ą': 'a', 'ć': 'c', 'ę': 'e',
... 'ł': 'l', 'ń': 'n', 'ó': 'o',
... 'ś': 's', 'ż': 'z', 'ź': 'z'}
>>>
>>> text = 'zażółć gęślą jaźń'
>>>
>>>
>>> result = map(lambda x: PL.get(x,x), text)
>>> ''.join(result)
'zazolc gesla jazn'
8.5.10. Use Case - 0x03¶
>>> people = [
... {'age': 21, 'name': 'Mark Watney'},
... {'age': 25, 'name': 'Melissa Lewis'},
... {'age': 18, 'name': 'Rick Martinez'},
... ]
>>>
>>>
>>> result = filter(lambda x: x['age'] >= 21, people)
>>> list(result)
[{'age': 21, 'name': 'Mark Watney'},
{'age': 25, 'name': 'Melissa Lewis'}]
8.5.11. Use Case - 0x04¶
>>> people = [
... {'is_staff': True, 'name': 'Mark Watney'},
... {'is_staff': False, 'name': 'Melissa Lewis'},
... {'is_staff': True, 'name': 'Rick Martinez'},
... ]
>>>
>>>
>>> can_login = filter(lambda x: x['is_staff'], people)
>>> list(can_login)
[{'is_staff': True, 'name': 'Mark Watney'},
{'is_staff': True, 'name': 'Rick Martinez'}]
8.5.12. Use Case - 0x05¶
>>> users = [
... 'mwatney',
... 'mlewis',
... 'rmartinez',
... 'avogel',
... 'bjohanssen',
... 'cbeck',
... ]
>>>
>>> staff = [
... 'mwatney',
... 'mlewis',
... 'ptwardowski',
... 'jjimenez',
... ]
>>>
>>>
>>> can_login = filter(staff.__contains__, users)
>>> list(can_login)
['mwatney', 'mlewis']
8.5.13. Use Case - 0x06¶
>>> from urllib.request import urlopen
>>>
>>> def fetch(url: str,
... on_success = lambda result: ...,
... on_error = lambda error: ...,
... ) -> None:
... try:
... result = urlopen(url).read()
... except Exception as error:
... return on_error(error)
... else:
... return on_success(result)
>>>
>>>
... fetch(
... url = 'https://python3.info',
... on_success = lambda result: print(result),
... on_error = lambda error: print(error))
8.5.14. Use Case - 0x07¶
>>> class Apply:
... def __init__(self, values):
... self.values = values
...
... def filter(self, fn):
... self.values = filter(fn, self.values)
... return self
...
... def map(self, fn):
... self.values = map(fn, self.values)
... return self
>>> DATA = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> result = (
... Apply(DATA)
... .filter(lambda x: x % 2 == 0)
... .map(lambda x: x ** 2)
... .map(lambda x: x + 1)
... .map(lambda x: x + 10)
... )
>>> list(result.values)
[15, 27, 47, 75]
8.5.15. Further Reading¶
8.5.16. Assignments¶
"""
* Assignment: Functional Lambda Chain
* Required: yes
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min
English:
1. Inline functions `odd()` and `cube()` with `lambda` expressions
2. Run doctests - all must succeed
Polish:
1. Zastąp funkcje `odd()` i `cube()` wyrażeniami `lambda`
2. Uruchom doctesty - wszystkie muszą się powieść
Hints:
* `mean = sum(...) / len(...)`
* type cast to `list()` before calculating mean to expand generator
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> type(result) is float
True
>>> result
245.0
"""
def odd(x):
return x % 2
def cube(x):
return x ** 3
# Inline lambda expressions
# type: float
result = range(0,10)
result = filter(odd, result)
result = map(cube, result)
result = list(result)
result = sum(result) / len(result)