Skip to content

Functions



Why we need functions

Analogy

  • You press a button on a coffee machine and coffee comes out.
  • You do not know how many grams it ground, what temperature the water hit, or how long it brewed. You do not care. You gave it an input and it gave you a result.
  • That is a function: a named box of work, with something going in and something coming out.
  • The person using it needs to know what goes in and what comes out. Nothing else.

Say you calculate a discount in four different places. Without a function that logic is copied four times, and when the rule changes you have to find all four. Miss one and your app quietly charges the wrong price.


What is a function?

Definition

A function is a named piece of work you can run whenever you like.

Defining it teaches the computer a new word. Calling it actually runs the work. A definition is the recipe; a call is cooking.

Beginners often write a beautiful function, run the file, see no output and get confused. The function was never called.


Parameters and arguments

A function gets more useful when you can hand it something.

Code
def greet(name):              # name is a parameter  print("Hello, " + name) greet("Amit")                 # "Amit" is an argument

The two words trip people up, so here is the short version. Parameter is the placeholder in the definition, the empty slot. Argument is the real value you drop into it at the call.

You can have several, and order matters. introduce(22, "Sara") is nonsense, and the computer will not stop you.


return, and why it is not print

return hands a result back to whoever called the function. Two things about it that everybody hits.

One: return ends the function immediately. Anything written after it never runs.

Two: print and return are completely different.

Code
def add_p(a, b):  print(a + b)         # shows it on screen, gives back nothing def add_r(a, b):  return a + b         # hands the value back to be used x = add_p(2, 3)          # prints 5, but x is Noney = add_r(2, 3)          # prints nothing, but y is 5

Note

print talks to the human. return talks to the rest of your program. If you want to use the answer, you need return.

A function with no return still returns something: Python gives back None, and Java and C call this a void function.


What actually gets passed in

A subtle one, and worth understanding early because it causes real bugs.

With simple values, the function gets a copy. It scribbles on the copy and the original never moves.

With lists and objects, the function gets the address. There is no copy, so both names point at the same list and the change is visible outside.

Code
def add_item(lst):  lst.append(4) items = [1, 2, 3]add_item(items)print(items)      # [1, 2, 3, 4]

The one-line rule: numbers and strings are safe to pass around; lists and objects can be modified by the function you hand them to. The memory chapter shows why.


Variables inside a function stay inside

Every call creates a fresh, private workspace, built when the function starts and thrown away when it finishes.

This is a feature rather than a limitation. It means you can use the name i or count inside a function without ever worrying about what the rest of the program is doing with those names.


Recursion

A function is allowed to call itself. That sounds like a trick and it is genuinely useful.

Code
def factorial(n):  if n == 1:          # base case, the stopping point      return 1  return n * factorial(n - 1)
Calling downeach call waits for the one below itAnswering back upeach frame finishes and disappearsfactorial(4)4 x 6 = 24factorial(3)3 x 2 = 6factorial(2)2 x 1 = 2factorial(1)returns 1The base case is where the pile stops growing and starts unwindingRemove it and the calls never stop, which is a stack overflow

Every recursive function needs two things: a base case where it stops calling itself, and a step that moves towards that base case.

Watch out

Miss either one and it calls itself forever. It will not run forever, though - it crashes with an error called stack overflow, and the memory chapter explains exactly why.


What makes a good function

  • One job. If the name needs the word "and", it is doing two things
  • A name that says what it does. calculate_total beats doStuff
  • Small. If it does not fit on your screen, it probably wants splitting
  • Few parameters. More than four usually means something needs rethinking
  • Predictable. Same input, same output, no surprises

Mistakes to watch for

Watch out

  • Defining a function and never calling it, then wondering why nothing happened.
  • Using print where you needed return.
  • Forgetting the brackets. greet is the function itself, greet() runs it.
  • Writing code after return and expecting it to run.
  • Wrong argument order.
  • Recursion with no base case.
  • Assuming a function cannot change a list you passed in. It can.

Quick recap

  • A function is named, reusable work: input in, output out
  • Defining teaches it, calling runs it
  • Parameters are the slots, arguments are the values
  • return sends a value back and ends the function on the spot
  • print is for people, return is for code
  • Variables inside a function are private to that call
  • Simple values are copied, lists and objects are passed by address
  • Recursion must have a base case

Key takeaway

A function takes something in, does one job, and hands something back. Print is for people, return is for code.