Functions & Scope
Picture a coffee machine. You press a button, it takes beans and water in, and it hands you a cup of coffee out. You don't need to know how it grinds or heats — you just need to know what to feed it and what you'll get back. A function is that same idea in code: a labeled, reusable block that takes some inputs, does work, and hands back a result.
def greet(name):
return "Hello, " + name + "!"
message = greet("Ada")
print(message) # Hello, Ada!
print(greet("Sam")) # Hello, Sam!
Notice the shape: def starts the definition, greet is the name you'll
call it by, (name) is the input it expects (called a parameter), and
return hands a value back to whoever called it. Write it once, call it as many times as
you like with different inputs.
💡 The Bottom Line
A function packages a piece of logic behind a name, so you write it once and reuse it anywhere.
Parameters are the inputs it accepts; return is the output it hands back. Everything
you build in Python beyond a few lines will be organized around functions like this.
Default arguments
You can give a parameter a fallback value, used automatically when the caller doesn't supply one:
def greet(name, greeting="Hello"):
return greeting + ", " + name + "!"
print(greet("Ada")) # Hello, Ada!
print(greet("Sam", "Welcome")) # Welcome, Sam!
This is extremely common in real Python code — it lets a function cover the typical case with no extra typing, while still letting a caller override the detail when they need to.
🙋 There Are No Dumb Questions
Q: What happens if a function doesn't have a return statement?
A: It still runs fine — it just implicitly returns None. This is completely normal
for functions that exist to do something (like printing, or saving a file) rather than
to compute something. Not every function needs to hand a value back.
Scope: where a variable is visible
Scope is the answer to "from where can I see this variable?" A variable created inside a function is local — it exists only while that function is running, and it's invisible outside it:
def calculate_total():
total = 100 # local to this function
return total
print(calculate_total()) # 100
print(total) # NameError: total is not defined
A variable created outside any function, at the top level of your script, is global — visible everywhere, including inside functions (for reading):
tax_rate = 0.08 # global
def price_with_tax(price):
return price * (1 + tax_rate) # reads the global just fine
print(price_with_tax(100)) # 108.0
⚠️ Watch Out
Reading a global variable from inside a function works automatically, but assigning to
a variable of the same name inside a function creates a brand-new local variable instead of
changing the global one — even if the names match. If you truly need to modify a global from
inside a function, you must declare it explicitly with the global keyword. Most of
the time, though, the better fix is to just return the new value instead of reaching outside the
function at all.
That last point is worth sitting with: functions that only depend on their parameters and only communicate through their return value are much easier to test, reuse, and reason about than ones that quietly reach out and change global state. It's a habit worth building early.
🔧 Try It Yourself
Write a function called discounted_price that takes a price and an
optional discount parameter (default 0.10 for 10%), and returns the
price after the discount is applied:
def discounted_price(price, discount=0.10):
return price * (1 - discount)
print(discounted_price(200)) # 180.0 (default 10% off)
print(discounted_price(200, 0.25)) # 150.0 (25% off)
Try calling it with just a price, then again passing a custom discount, and confirm both give the results you expect.
✅ Quick Check
A variable is created inside a function using a plain = assignment. Where can it be
accessed by default?
Next up: functions decide what to run, but not yet when or how many times.
Lesson 6 covers control flow — if/elif/else, and the
for and while loops that repeat work for you.