Functions
Naming a piece of behaviour
You have been calling functions since your first program: print(), len(), int(), range(). A function is a named block of code that you can run whenever you need it. Now you will write your own.
def greet():
print("Welcome to the Python course.")
print("Let's begin.")
greet()
greet()
The parts:
deftells Python you are defining a function.greetis the name, following the same rules as variable names.- The empty brackets say it takes no information from the caller.
- The colon and indented block hold the body.
Defining a function does not run it. Python reads the definition, remembers it, and moves on. The body runs only when you call it by writing greet() — including the brackets. Writing greet without brackets refers to the function itself rather than running it.
Parameters and arguments
Most functions need data to work on. Names listed in the definition are parameters; the actual values you pass in when calling are arguments.
def greet(name):
print(f"Hello, {name}. Welcome aboard.")
greet("Priya")
greet("Rahul")
def show_result(name, marks):
status = "Pass" if marks >= 40 else "Fail"
print(f"{name}: {marks}/100 - {status}")
show_result("Anjali", 76)
show_result("Vikram", 31)
Arguments are matched to parameters by position: the first argument goes to the first parameter, and so on. Passing the wrong number raises TypeError, which is a useful safeguard.
That "Pass" if marks >= 40 else "Fail" is a conditional expression — a compact if/else that produces a value. Use it only when it stays this readable.
Returning a value
Printing and returning are different things, and confusing them is the most common early misunderstanding about functions.
print shows something on screen. return hands a value back to whoever called the function, so it can be stored, compared or used in further arithmetic.
def calculate_percentage(marks, total):
return marks / total * 100
def is_pass(percentage):
return percentage >= 40
result = calculate_percentage(342, 500)
print(f"Percentage: {result:.2f}%")
print("Passed:", is_pass(result))
if calculate_percentage(180, 500) < 40:
print("Below the pass mark.")
scores = [calculate_percentage(m, 100) for m in [78, 65, 91]]
print(scores)
A function that only prints cannot be used in any of those ways. As a rule: calculate and return in one function; print in the caller. That keeps the calculation reusable — the same function can serve a console program, a web page or a file report.
return also ends the function immediately. Any lines after it are never reached, which lets you exit early:
def grade_of(marks):
if marks < 0 or marks > 100:
return "Invalid"
if marks >= 90:
return "O"
if marks >= 75:
return "A"
if marks >= 40:
return "P"
return "F"
for m in [95, 80, 55, 20, 150]:
print(m, "->", grade_of(m))
A function with no return statement returns None. That is not an error, but if you expected a value and got None, a missing return is the first thing to check.
Default arguments
A parameter can be given a default value, used when the caller does not supply one.
def calculate_percentage(marks, total=100):
return marks / total * 100
print(calculate_percentage(78))
print(calculate_percentage(342, 500))
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Priya")
greet("Priya", "Good morning")
greet("Priya", greeting="Namaste")
Parameters with defaults must come after those without, otherwise Python cannot tell which argument is which. def f(a=1, b) is a syntax error.
The last call uses a keyword argument, naming the parameter explicitly. That is worth doing when a call would otherwise be cryptic: send(True, False, True) tells a reader nothing, while send(urgent=True, retry=False, log=True) tells them everything.
def add_item(item, basket=[]): behaves surprisingly: the default list is created once, when the function is defined, and is then shared by every call that relies on it. Items accumulate across calls. Use basket=None and create a fresh list inside the function instead.
Scope
Variables created inside a function exist only inside it. This is called local scope.
def compute():
inner = 42
print("Inside the function:", inner)
compute()
# print(inner) here would raise NameError
message = "I am global"
def show():
print("Function can read:", message)
show()
def try_change():
message = "changed locally"
print("Inside:", message)
try_change()
print("Outside:", message)
The function can read a variable defined outside it, but assigning to that name inside the function creates a separate local variable rather than modifying the outer one. The outer message is unchanged.
This isolation is a feature, not a restriction. It means you can write a function without worrying that its variable names clash with the rest of the program, and it means a function's behaviour depends only on its arguments. To get a value out of a function, use return — not global variables.
Why functions matter
Beyond avoiding repetition, three reasons:
- They give a name to an idea.
is_eligible(attendance, marks)states intent in a way that four lines of inline conditions do not. - They make testing possible. You can call a function with known inputs and check its output. You cannot test a paragraph in the middle of a script.
- They limit how much you must hold in your head. Once a function is written and works, you use it by its name and forget its internals. This is how programs larger than one screen stay manageable.
Common mistakes
Calling a function without brackets. greet refers to the function object; greet() runs it. A line that seems to do nothing often just has missing brackets.
Printing when you meant to return.
def add(a, b):
print(a + b)
result = add(2, 3) # result is None
print displays; return hands the value back. Use return a + b.
Defining a function after calling it. Python executes top to bottom, so a call placed above the def raises NameError. Define functions before you use them.
Expecting a function to change a global variable by assigning to it. Assignment inside a function creates a local name. Return the new value and reassign in the caller.
Mismatched argument count. Calling show_result("Anjali") when the function needs two parameters raises TypeError: missing 1 required positional argument. The error message names what is missing.
Putting a default parameter before a required one. def f(a=1, b): is a SyntaxError. Required parameters come first.
Unreachable code after return. Anything written after a return in the same block never runs. If a calculation seems to be ignored, check whether a return above it is executing first.
Practice
Try the function exercises on the practice page.