ShareYourGroupShareYourGroupPython

Lesson 16 of 21 · about 7 minutes with the exercises

Function arguments and scope

The last lesson covered the basic shape of a function. This one adds the features that make functions pleasant to use, and explains a rule that puzzles every beginner sooner or later: which variables a function can see.

Default values

A parameter can have a default value. If the caller leaves that argument out, the default is used.

def greet(name, greeting="Namaste"):
    print(f"{greeting}, {name}!")

greet("Asha")
greet("Ravi", "Good morning")

Output

Namaste, Asha!
Good morning, Ravi!

Parameters with defaults must come after the ones without. You have been using defaults all along: print has sep=" " and end="\n", and round keeps zero decimal places unless told otherwise.

Keyword arguments

Normally arguments are matched by position: first to first, second to second. You can instead name the parameter in the call. Then the order does not matter, and the call explains itself.

def bill(amount, tax_percent=5, tip=0):
    return amount + amount * tax_percent / 100 + tip

print(bill(1000))
print(bill(1000, 18))
print(bill(1000, tip=50))
print(bill(tip=50, amount=1000, tax_percent=12))

Output

1050.0
1180.0
1100.0
1170.0

The third call skips tax_percent and sets only tip. Without keyword arguments that would be impossible. In a call, positional arguments come first and keyword arguments after them.

Any number of arguments

A star before a parameter name collects all the extra positional arguments into a tuple. The usual name is args.

def average(*numbers):
    return sum(numbers) / len(numbers)

print(average(10, 20))
print(average(5, 7, 9, 11))

Output

15.0
8.0

This is how print and max accept as many values as you give them. You will read *args in other people's code long before you need to write it.

Scope: where a variable lives

A variable made inside a function is local. It exists only while the function runs and cannot be seen from outside.

def make_total():
    total = 100

make_total()
print(total)

What Python says

Traceback (most recent call last):
  File "example.py", line 5, in <module>
    print(total)
          ^^^^^
NameError: name 'total' is not defined

The function ran, made its total, and threw it away when it finished. Outside the function, no such name exists. This is a good thing. It means two functions can each use a variable called total or i without disturbing one another.

A variable made outside all functions is global. A function can read it.

rate = 18

def with_tax(amount):
    return amount + amount * rate / 100

print(with_tax(500))

Output

590.0

Assigning inside a function makes a new local variable

A function can read a global, but assigning to that name inside the function does not change the global. It makes a separate local variable with the same name.

count = 0

def add_one():
    count = 1
    print("inside:", count)

add_one()
print("outside:", count)

Output

inside: 1
outside: 0

The outer count is still 0. If you try to both read and assign the same global name inside a function, Python stops with an UnboundLocalError. There is a keyword, global, that lets a function change a global variable, but it makes programs hard to follow. The clean way is to pass values in as arguments and get results back with return.

def add_one(count):
    return count + 1

count = 0
count = add_one(count)
count = add_one(count)
print(count)

Output

2

Lists passed to functions

There is one case where a function does change something outside itself. A list is mutable, and a function that receives a list gets the same list, not a copy. If it appends to the list, the caller sees the change.

def add_bonus(marks):
    for i in range(len(marks)):
        marks[i] += 5

class_marks = [60, 72, 88]
add_bonus(class_marks)
print(class_marks)

Output

[65, 77, 93]

Sometimes that is what you want. If it is not, build and return a new list instead.

def with_bonus(marks):
    result = []
    for m in marks:
        result.append(m + 5)
    return result

original = [60, 72, 88]
print(with_bonus(original))
print(original)

Output

[65, 77, 93]
[60, 72, 88]

A function as a value

A function name without brackets is a value like any other. You can pass it to another function. That is what you did with key=len and key=str.lower when sorting.

def last_letter(word):
    return word[-1]

words = ["mango", "kiwi", "banana", "fig"]
print(sorted(words, key=last_letter))

Output

['banana', 'fig', 'kiwi', 'mango']

sorted calls last_letter once for each word and sorts by the results.

Guidelines for good functions

Try it yourself

Exercise 1. Write power(base, exponent=2) that returns the base raised to the exponent. Print power(5) and power(2, 10).

Show one answer
def power(base, exponent=2):
    return base ** exponent

print(power(5), power(2, 10))

Output

25 1024

Exercise 2. Write total_price(price, quantity=1, discount=0) where discount is in percent. Print the cost of 3 items at 200 rupees with a 10 percent discount, using keyword arguments.

Show one answer
def total_price(price, quantity=1, discount=0):
    amount = price * quantity
    return amount - amount * discount / 100

print(total_price(200, quantity=3, discount=10))

Output

540.0

Exercise 3. Write longest(*words) that returns the longest of any number of words. Test it with four words.

Show one answer
def longest(*words):
    return max(words, key=len)

print(longest("tea", "coffee", "buttermilk", "milk"))

Output

buttermilk

Examples on this page were run with Python 3.14 on 12 November 2026. The output shown is the real output.

This is an independent learning site. It is not connected with or approved by the Python Software Foundation. "Python" is a trademark of the Python Software Foundation and is used here only to name the language.