🚀 Alguni Start learning

Unit 16: Functions as Things

Passing functions around.

Unit 16 of 31 in Python for kids. Its 7 lessons are Functions Are Values Too, Tiny Functions, Map and Filter, As Many As You Like, Functions That Build Functions, Decorators and Function Wizard — below is everything each one explains, and a question or two from it to try.

Every sample on this page was run through real Python before it shipped, and prints exactly what it says it prints.

This unit opens with a fortnight’s trial of everything — no card needed — or with a family plan, bought in the iPhone app. The first two units of every track are free for ever. Try it in the app.

📦 Functions Are Values Too

A function without its brackets

Write shout() and it runs. Write shout on its own and you get the function itself — which you can put in a variable, exactly like a number.

Python

def shout(word):
    return word.upper()

noisy = shout
print(noisy("hello"))

It prints

HELLO

Handing a function to a function

If a function is a value, you can pass one in as an argument. Now apply can do *anything* — it depends entirely on what you give it.

Python

def shout(word):
    return word.upper()

def whisper(word):
    return word.lower()

def apply(fn, word):
    return fn(word)

print(apply(shout, "Hi"))
print(apply(whisper, "Hi"))

It prints

HI
hi

This is what key= has been doing

You have used this already. sorted takes a function and calls it on each item to decide what to sort by.

Python

def length(word):
    return len(word)

words = ["pear", "fig", "banana"]
print(sorted(words, key=length))

It prints

['fig', 'pear', 'banana']

Try it yourself

What is the difference between shout and shout("hi")?

  • Nothing
  • The first is the function itself; the second runs it and gives the answer
  • The first is a mistake
  • The second is faster

What does this print?

Python

def double(n):
    return n * 2

def run_twice(fn, n):
    return fn(fn(n))

print(run_twice(double, 3))

Answer them in the app

🐜 Tiny Functions

A function with no name

When a function is one short line and you only need it once, lambda writes it in place. There is no def, no name and no return — the answer is just the bit after the colon.

Python

double = lambda n: n * 2
print(double(5))

It prints

10

The same function, both ways

These two are exactly the same thing. lambda is only ever a shorter way of writing a very small def.

Python

def add_a(x, y):
    return x + y

add_b = lambda x, y: x + y

print(add_a(2, 3))
print(add_b(2, 3))

It prints

5
5

Where you actually meet it

This is the key=lambda you have seen before. It says "when sorting, look at the age" — too small a job to be worth a whole def.

Python

kids = [("Ada", 11), ("Sam", 7)]
for kid in sorted(kids, key=lambda pair: pair[1]):
    print(kid[0])

It prints

Sam
Ada

Try it yourself

What does this print?

Python

square = lambda n: n * n
print(square(6))

When should you NOT use a lambda?

  • Never, lambdas are always better
  • When the function is more than a line, or you want to give it a helpful name
  • When it takes two arguments
  • When it returns a number

Answer them in the app

🗺️ Map and Filter

Do the same thing to everything

map runs a function on every item and gives back the results. Wrap it in list() to see them.

Python

nums = [1, 2, 3]
print(list(map(lambda n: n * 2, nums)))

It prints

[2, 4, 6]

Keep only the ones you want

filter keeps an item when the function says True about it, and drops it otherwise.

Python

nums = [1, 2, 3, 4, 5]
print(list(filter(lambda n: n > 3, nums)))

It prints

[4, 5]

Squashing a list to one answer

reduce folds a list down to a single value, two at a time. It lives in functools, and honestly sum() is nicer when it will do.

Python

from functools import reduce

print(reduce(lambda a, b: a + b, [1, 2, 3, 4]))
print(sum([1, 2, 3, 4]))

It prints

10
10

Try it yourself

What is the difference between map and filter?

  • map changes every item; filter throws some items away
  • They are the same
  • map is faster
  • filter changes items too

What does this print?

Python

words = ["a", "bb", "ccc"]
print(list(map(len, words)))

Answer them in the app

🎁 As Many As You Like

A function that takes any number of things

A * before a parameter collects however many arguments were passed into a tuple. The name args is just tradition — the * does the work.

Python

def total(*nums):
    return sum(nums)

print(total(1, 2))
print(total(1, 2, 3, 4))
print(total())

It prints

3
10
0

Named extras with two stars

**kwargs collects any *named* arguments into a dictionary.

Python

def describe(**facts):
    for key, value in facts.items():
        print(key, "=", value)

describe(name="Ada", age=9)

It prints

name = Ada
age = 9

Ordinary arguments can come first

You can have normal parameters and then *args to catch anything else.

Python

def greet(greeting, *names):
    for name in names:
        print(greeting, name)

greet("Hi", "Ada", "Sam")

It prints

Hi Ada
Hi Sam

Try it yourself

What does this print?

Python

def count_them(*things):
    print(len(things))

count_them("a", "b", "c")
count_them()

What does *nums give you inside the function?

  • A single number
  • A tuple holding all the arguments that were passed
  • A dictionary
  • The number of arguments

Answer them in the app

🏗️ Functions That Build Functions

A function defined inside another

A function can define another one inside itself, and hand it back. The inner one remembers the values it grew up with.

Python

def make_adder(n):
    def add(m):
        return n + m
    return add

add5 = make_adder(5)
print(add5(3))
print(add5(10))

It prints

8
15

A factory for functions

Each call makes a *different* function, with its own remembered value.

Python

def times(n):
    return lambda m: n * m

double = times(2)
triple = times(3)
print(double(5))
print(triple(5))

It prints

10
15

Try it yourself

How does add still know what n was, after make_adder has finished?

  • It does not, it guesses
  • The inner function keeps hold of the values around it when it was made
  • n is global
  • Python re-runs make_adder each time

What does this print?

Python

def power(exp):
    def go(n):
        return n ** exp
    return go

square = power(2)
cube = power(3)
print(square(4))
print(cube(2))

Answer them in the app

🎀 Decorators

Wrapping a function in another

A decorator takes a function and gives back a new one that does a bit more. You have already used @property and @classmethod — this is what that @ means.

Python

def loud(fn):
    def wrapper():
        print("--- start ---")
        fn()
        print("--- end ---")
    return wrapper

@loud
def hello():
    print("hello")

hello()

It prints

--- start ---
hello
--- end ---

What the @ is short for

These two do exactly the same thing. The @ is only a tidier way of saying "run my function through this one and keep the result".

Python

def loud(fn):
    def wrapper():
        print("hi!")
        fn()
    return wrapper

def bye():
    print("bye")

bye = loud(bye)
bye()

It prints

hi!
bye

Letting arguments through

If the wrapped function takes arguments, the wrapper must pass them along — which is exactly what *args and **kwargs are for.

Python

def twice(fn):
    def wrapper(*args, **kwargs):
        fn(*args, **kwargs)
        fn(*args, **kwargs)
    return wrapper

@twice
def greet(name):
    print("Hi " + name)

greet("Ada")

It prints

Hi Ada
Hi Ada

Try it yourself

What does a decorator take, and give back?

  • A number, and a number
  • A function, and a new function
  • A class, and an object
  • Nothing, and nothing

What does this print?

Python

def shout(fn):
    def wrapper(word):
        return fn(word).upper()
    return wrapper

@shout
def say(word):
    return "i said " + word

print(say("hi"))

Answer them in the app

🏆 Function Wizard

Try it yourself

What does this print?

Python

add = lambda a, b: a + b
print(list(map(lambda n: add(n, 10), [1, 2])))

Answer it in the app