Recall from lesson 7-1 that ages.get("Linus", 0) returned 0 rather than crashing. The second value is the default to return when the key is missing.
That is the whole contract of get(key, default): supply a fallback and a missing key becomes an ordinary result instead of a KeyError.
Look at the shape of what happened there, because it is the subject of this unit. Values went in through the parentheses, work happened out of sight, and a result came back for the caller to use. Every function follows that in-and-out shape, and from here on you will be writing your own.
A function is a named, reusable block
By now some of your programs repeat the same few lines with tiny changes, and repetition is where bugs breed: fix one copy, forget the other. Functions exist to kill that duplication: define the step once, name it, reuse it everywhere, fix it in one place. Every library you will ever import is a pile of functions somebody defined, so this lesson is also how you will read other people's code.
You have been calling functions all course: print(), len(), input(). Now you get to define your own with def:
def area(w, h): return w * h
areais the function's name.wandhare parameters: placeholder variables for the inputs.returnsends a value back to whoever called.
Defining runs nothing. The body executes only when you call it, and the values you pass, the arguments, get assigned to the parameters:
print(area(3, 4)) # w becomes 3, h becomes 4, back comes 12
Write a function whenever the same few lines would otherwise be pasted twice, or when naming a step (area, greet) makes the program read like a plan.
Two definitions, three calls
Definitions come first and produce no output on their own. Nothing runs until the calls at the bottom.
def greet(name): return f"Hello, {name}!" def area(w, h): return w * h print(greet("Ada")) print(greet("Grace")) print(area(3, 4))
Output
Hello, Ada!
Hello, Grace!
12The two calls to greet show the point of a parameter. The same body ran twice, once with name holding "Ada" and once with "Grace", so one definition covers every name that will ever be passed to it. The area call assigns positionally, 3 to w and 4 to h, and its return hands 12 back to the surrounding print.
Note that neither function prints anything itself. They return values and leave the decision about displaying them to the caller, which is what makes them reusable in a context where printing would be wrong.
Defining circumference
A definition followed immediately by a call is the smallest complete example of the pattern.
def circumference(r): return 2 * 3.14159 * r print(circumference(10))
Output
62.8318The body is a single return, which computes the value and sends it back. Replacing a placeholder pass with a real return is the usual move when filling in a stub, and the difference matters: a function with no return hands back None, so print would show None rather than a number.
The name is doing real work too. Reading circumference(10) at the call site says what the number means, which a bare 2 * 3.14159 * 10 sitting inline would not.
Default values and keyword arguments
A parameter can carry a default value, written with = in the def line. The default is used whenever the caller leaves that argument out. Defaults exist so the common call stays short while the unusual call stays possible:
def greet(name, greeting="Hello"): return f"{greeting}, {name}!" greet("Ada") # Hello, Ada! greet("Ada", "Welcome") # Welcome, Ada!
You can also name arguments at the call site, called keyword arguments: greet(greeting="Hi", name="Grace"). With a keyword argument the name, not the position, decides which parameter receives the value. Real code uses this to keep calls readable, and print itself accepts one: print("a", "b", sep="-") prints a-b, where sep replaces print's default separator, a space.
Defining power with a default
A default value lets one function serve both the common case and the general one.
def power(base, exp=2): return base ** exp print(power(5)) print(power(2, 10))
Output
25 1024
The first call passes only base, so exp falls back to 2 and the function computes 5 ** 2, giving 25. The second call supplies both arguments, and the passed 10 overrides the default to compute 2 ** 10.
The ordering in the def line is not optional. Parameters with defaults have to come after those without, since Python fills positional arguments left to right and would have no way to tell which one was skipped otherwise.