Functions that take functions
sorted, min and max get a function as an argument and call it themselves. Your own functions can do the same. A parameter can hold a function just as it holds a number or a list, and you call it with parentheses, like any function:
def apply_to_all(numbers, operation):
results = []
for number in numbers:
results.append(operation(number))
return results
def double(x):
return 2 * x
print(apply_to_all([1, 2, 3], double))
print(apply_to_all([1, 2, 3], lambda x: x * x))
print(apply_to_all([-4, 5, -6], abs))
[2, 4, 6]
[1, 4, 9]
[4, 5, 6]apply_to_all doesn't know what operation does. It only expects a function that takes one number. Which function runs is up to the caller: a named one, a lambda or one of Python's own, such as abs, which gives a number without its minus sign. That makes one short function useful for many jobs.
The function you pass must accept the arguments it will be called with. Here operation gets two:
def combine(a, b, operation):
return operation(a, b)
print(combine(12, 4, lambda x, y: x + y))
print(combine(12, 4, lambda x, y: x / y))
print(combine(12, 4, max))
16
3.0
12A common slip
Pass the function, not the result of calling it. Here double(5) runs first and gives 10, so operation is the number 10, and Python can't call a number:
def apply_to_all(numbers, operation):
results = []
for number in numbers:
results.append(operation(number))
return results
def double(x):
return 2 * x
print(apply_to_all([1, 2, 3], double(5)))
Traceback (most recent call last):
File "main.py", line 12, in <module>
print(apply_to_all([1, 2, 3], double(5)))
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "main.py", line 4, in apply_to_all
results.append(operation(number))
~~~~~~~~~^^^^^^^^
TypeError: 'int' object is not callable"Not callable" means the value isn't a function. Write double, without parentheses.
A function as a rule
A function argument is a neat way to pass a rule: which items to keep, to count or to look for. The function that gets the rule does the looping, and the caller only says what counts. So one count_matching can count long words, words with an o or negative numbers:
def count_matching(items, condition):
count = 0
for item in items:
if condition(item):
count += 1
return count
def is_long(word):
return len(word) > 4
words = ["sun", "moon", "star", "comet", "planet", "nebula"]
print(count_matching(words, is_long))
print(count_matching(words, lambda word: "o" in word))
print(count_matching([3, -1, 8], lambda n: n < 0))
3
2
1