Definition
Write a program of any length and the same block of code keeps coming back: working out a discount, formatting a date. You copy it once, then you copy it again. The day the rule changes, you have to track down every copy, and one of them always slips through. That is the annoyance a function makes go away.
A function groups under a single name a block of code meant to be replayed. It optionally takes data in, its arguments, and hands back a result with return. You call it by typing its name followed by brackets, and the rule it holds now lives in one place only.
In Python it is declared with def. Here is the smallest one that is genuinely useful: a name, a required argument, an optional argument, a result handed back.
def discount(price, rate=0.1):
"""Apply a discount and round to the cent."""
return round(price * (1 - rate), 2)
discount(100) # 90.0, the default rate applies
discount(100, 0.25) # 75.0, we supply our own rateThe rate=0.1 in the declaration is a default value: it makes that argument optional, which is why the first call works with a single number.
The string sitting just underneath is the docstring. It documents the function where it is read, and editors pick it up to show it to you at call time.
What you gain from it
The first gain is the one just described: repetition stops. A bug fixed inside a function is fixed everywhere it is called, including the places you had forgotten about.
The second is less visible and more valuable over time: a function gives a name to an intent. The line discount(price) takes no effort to read, where three lines of multiplication and rounding force you to reconstruct what they compute. And a name, unlike a comment, does not quietly go out of date.
The third is isolation. What happens inside depends only on the arguments received, never on the rest of the program. So you can call the function with values of your choosing and check its result without starting the application: that is exactly what an automated test does.
Hence a simple rule: a function does one thing. If its name holds an "and", it probably does two, and would gain from being split.
What goes in: the arguments
Arguments are supplied in the order of the declaration, or by name. The second form makes the call longer but far clearer as soon as several values are involved: discount(100, rate=0.25) can be understood without reopening the function.
A default value makes an argument optional. This is the usual way of growing a function without breaking what exists: calls already written, which know nothing of the new argument, keep working.
That leaves the case where you do not know how many arguments you will receive. Two forms cover it: args for any number of ordinary arguments, kwargs for any number of named ones.
A default value is evaluated once, at declaration time, not on every call. With a mutable object such as items=[], the very same list is reused from one call to the next, and it fills up with nothing to warn you. The convention is to write items=None and create the list inside the body of the function.
What comes out: the return
return ends the call and hands a value back to the place the function was called from. That value becomes ordinary data again: you can store it in a variable, pass it to another function, or print it.
A function without return hands back None. That is not a flaw: it is the normal case for a function that acts instead of computing, such as saving a file. You are not asking it for a result, you are asking it for an effect.
Mixing up printing and returning is the most common beginner mistake. A function ending in print(result) instead of return result does show the right value on screen, but hands back None. The program only breaks further down, when that result is reused, and you look for the fault in the wrong place.
What stays inside
A variable created inside a function exists only for the duration of the call, then disappears. It stays invisible from outside, which is good news: you can use a counter i in ten functions without any of them treading on each other. This zone in which a name lives is called scope.
The reverse is not true. A function can read a variable defined at module level, but it cannot change it without saying so with global. Python refuses to do it behind your back.
Take that as a signal: a function that changes an outside variable loses the isolation that made it valuable. Taking the value in as an argument and handing the new version back almost always gives code that is simpler to read.
Frequently asked questions
What is the difference between a function and a method?
A method is a function defined inside a class and attached to an object, which it receives as its first argument under the name self. Everything you have just read still holds for it: arguments, defaults, return, scope. The difference lies in what it has at hand.
How many arguments should a function take?
As few as possible. Past four or five, the call becomes hard to read and easy to get out of order. A long list often signals that the arguments form a single notion, an address or an order: a dictionary or a dataclass expresses that better.
Can a function call another one?
Yes, without limit, and it can even call itself: that is recursion. You then need a stopping case, otherwise Python halts the program with a RecursionError after about a thousand nested calls. Splitting a program into short functions is the skill that separates a script that works from code you can pick up again six months later, and our Python course devotes a whole part to it.