What if you want a function to accept any number of arguments?
The built-in max
function allows that:
max(1, 2) # 2
max(10, 30, 20) # 30
max(-2, 33, -40, 400, 321) # 400
That's possible by using the *args
syntax in the function definition.
def max(*args):
# Do something with *args
One way to use *args
is to send those arguments into
another function.
def min_and_max(*args):
return min(*args), max(*args)
min_and_max(-2, 33, -40, 400, 321) # -40, 400
A HOF can return a function that can be called with any number of arguments, and then forward those arguments inside the returned function.
def printed(f):
def print_and_return(*args):
result = f(*args)
print('Result:', result)
return result
return print_and_return
printed_max = printed(max)
printed_max(-2, 33, -40, 400, 321)
Currying: Converting a function that takes multiple arguments into a single-argument higher-order function.
A function that currys any two-argument function:
def curry2(f):
def g(x):
def h(y):
return f(x, y)
return h
return g
from operator import add
make_adder = curry2(add)
make_adder(2)(3)
curry2 = lambda f: lambda x: lambda y: f(x, y)
Whenever another function requires a function that only takes one argument:
def transform_numbers(num1, num2, num3, transform):
return transform(num1), transform(num2), transform(num3)
transform_numbers(3, 4, 5, curry2(add)(60))
Alternate approach:
transform_numbers(3, 4, 5, lambda x: add(60, x))
Turning a generalized function into a specialized function:
def html_tag(tag_name, text):
return "<" + tag_name + ">" + text + "</" + tag_name + ">"
p_tag = curry2(html_tag)("p")
p_tag("hello hello")
Alternate approach:
import functools
p_tag = functools.partial(html_tag, "p")
p_tag("hello hello")
🥦 It's good for you!
CS61A introduces many concepts that aren't standard Python practice, but that show up in other languages.
Currying is a very common practice in functional programming languages like Haskell or Clojure.
WWPD exercises test our understanding of how Python evaluates code and what it chooses to display in the shell.
The expression | Evaluates to | Interactive output |
---|---|---|
5
| 5
| 5
|
print(5)
| None
| 5
|
print(print(5))
| None
| 5 None
|
>> 5
5
>>> print(5)
5
>>> print(print(5))
5
None
def delay(arg):
print('delayed')
def g():
return arg
return g
The expression | Evaluates to | Interactive output |
---|---|---|
delay(6)()
| 6
| delayed 6
|
delay(delay)()(6)()
| 6
|
delayed delayed 6
|
print(delay(print)()(4))
| None
|
delayed 4 None
|
def pirate(arggg):
print('matey')
def plunder(arggg):
return arggg
return plunder
The expression | Evaluates to | Interactive output |
---|---|---|
pirate('treasure')('scurvy')
| 'scurvy'
| matey 'scurvy'
|
add(pirate(3)(square)(4), 1)
| 17
|
matey 17
|
pirate(pirate(pirate))(5)(7)
| Error
|
matey matey Error
|
A name evaluates to the value bound to that name in the earliest frame of the current environment in which that name is found.
def horse(mask):
horse = mask
def mask(horse):
return horse
return horse(mask)
mask = lambda horse: horse(2)
horse(mask)
horse | ||
mask | ||
Return value |
Return value |
Return value |
def remove(n, digit):
"""Return digits of non-negative N
that are not DIGIT, for some
non-negative DIGIT less than 10.
>>> remove(231, 3)
21
>>> remove(243132, 2)
4313
"""
kept = 0
digits = 0
while ___________________________:
last = n % 10
n = n // 10
if __________________________:
kept = __________________
digits = ________________
return ___________________________
def remove(n, digit):
"""Return digits of non-negative N
that are not DIGIT, for some
non-negative DIGIT less than 10.
>>> remove(231, 3)
21
>>> remove(243132, 2)
4313
"""
kept = 0
digits = 0
while n > 0:
last = n % 10
n = n // 10
if last != digit:
kept = kept + (last * 10 ** digits)
digits = digits + 1
return kept