DEV Community

Cover image for Python - Functions: Defining, Calling, Parameters, Return Values & Scope
Mary Ngure
Mary Ngure

Posted on

Python - Functions: Defining, Calling, Parameters, Return Values & Scope

Functions are one of the first "real" programming concepts you hit in Python, and they're also one of the most important.
Once you understand how to package logic into a function, you stop repeating yourself and start building code that's actually reusable and testable.

1. Defining and Calling Functions

A function is a named block of code that runs only when you call it. You define one with the def keyword:

def greet():
    print("Hello, welcome!")
Enter fullscreen mode Exit fullscreen mode

This creates a function called greet, but nothing happens yet, defining a function just stores the instructions. To actually run it, you call it by using its name followed by parentheses:

greet()  # Output: Hello, welcome!
Enter fullscreen mode Exit fullscreen mode

You can call the same function as many times as you want:

greet()
greet()
greet()
Enter fullscreen mode Exit fullscreen mode

Each call executes the function's body from the top.

2. Parameters and Arguments

Most useful functions need input to work with.
Parameters are the placeholders you define in the function signature; arguments are the actual values you pass in when calling the function.

def greet(name):
    print(f"Hello, {name}!")

greet("Mary")   # "name" is the parameter, "Mary" is the argument
greet("James")
Enter fullscreen mode Exit fullscreen mode

Multiple parameters

def describe_dataset(name, rows, columns):
    print(f"{name}: {rows} rows, {columns} columns")

describe_dataset("Sales Data", 5000, 12)
Enter fullscreen mode Exit fullscreen mode

Default parameter values

You can give a parameter a default value, so it's optional when calling the function:

def describe_dataset(name, rows, columns=1):
    print(f"{name}: {rows} rows, {columns} columns")

describe_dataset("Sales Data", 5000)          # uses default columns=1
describe_dataset("Sales Data", 5000, 12)      # overrides the default
Enter fullscreen mode Exit fullscreen mode

Keyword arguments

Arguments can be passed by position or by name. Using names (keyword arguments) makes calls clearer, especially with several parameters:

describe_dataset(rows=5000, columns=12, name="Sales Data")
Enter fullscreen mode Exit fullscreen mode

Order doesn't matter when you use keyword arguments — Python matches them by name.

*args and **kwargs

Sometimes you don't know in advance how many arguments will be passed. *args collects extra positional arguments into a tuple, and **kwargs collects extra keyword arguments into a dictionary:

def sum_values(*args):
    return sum(args)

print(sum_values(1, 2, 3))       # 6
print(sum_values(10, 20, 30, 40)) # 100

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Mary", role="Data Analyst", city="Nairobi")
Enter fullscreen mode Exit fullscreen mode

3. Return Values

A function that only prints something is limited, the result disappears once printed.
The return statement sends a value back to wherever the function was called, so you can store it, reuse it, or pass it to another function.

def add(a, b):
    return a + b

result = add(4, 5)
print(result)  # 9
Enter fullscreen mode Exit fullscreen mode

Without return, a function implicitly returns None:

def greet(name):
    print(f"Hello, {name}!")

value = greet("Mary")  # prints "Hello, Mary!"
print(value)            # None
Enter fullscreen mode Exit fullscreen mode

Returning multiple values

Python lets you return more than one value at once, packed as a tuple:

def get_min_max(numbers):
    return min(numbers), max(numbers)

low, high = get_min_max([4, 9, 1, 7, 3])
print(low, high)  # 1 9
Enter fullscreen mode Exit fullscreen mode

A practical example combining parameters and return values

def calculate_average(numbers):
    if len(numbers) == 0:
        return 0
    return sum(numbers) / len(numbers)

scores = [85, 90, 78, 92, 88]
average = calculate_average(scores)
print(f"Average score: {average}")  # Average score: 86.6
Enter fullscreen mode Exit fullscreen mode

Here, the function takes data in (numbers), does the work, and hands a usable result back out via return, rather than just printing it and losing it.

4. return vs. print: What's the Difference?

New Python learners often mix these up because both can show a value on the screen but they do fundamentally different things.

  • print() displays a value to the console. It's for humans watching the program run. Once printed, that value is gone — it isn't stored anywhere the program can use again.

  • return sends a value back out of the function to whatever code called it. It's for the program itself. The returned value can be stored in a variable, passed to another function, or used in further calculations.

def add_print(a, b):
    print(a + b)

def add_return(a, b):
    return a + b

result1 = add_print(5, 3)   # prints "8" to the console
result2 = add_return(5, 3)  # returns 8, nothing printed

print(result1)  # None  -> add_print never returned a value
print(result2)  # 8     -> add_return handed the value back
Enter fullscreen mode Exit fullscreen mode

add_print shows 8 on screen, but as far as the rest of the program is concerned, it produced nothing — result1 ends up None.
add_return, on the other hand, actually hands the value 8 back, so result2 holds a usable number you can do more with:

total = add_return(5, 3) + add_return(10, 2)
print(total)  # 20
Enter fullscreen mode Exit fullscreen mode

Trying the same thing with add_print would fail, since add_print(5, 3) evaluates to None, and you can't add None to a number.

Rule of thumb: use print() when you just want to see a value while debugging or communicating with a user; use return when the function's result needs to be used elsewhere in your code.
In most real programs, functions should return values rather than print them, so the caller decides what to do with the result — whether that's printing it, saving it, or feeding it into another function.

5. Scope

Scope determines where in your code a variable can be accessed. Understanding scope prevents a lot of confusing bugs, especially "why isn't this variable available here?" moments.

Local scope

Variables created inside a function only exist inside that function. They are local to it and disappear once the function finishes running.

def calculate_total(price, quantity):
    total = price * quantity  # local variable
    return total

print(calculate_total(100, 3))  # 300
print(total)  # NameError: total is not defined
Enter fullscreen mode Exit fullscreen mode

total was created inside calculate_total, so it can't be accessed outside of it.

Global scope

Variables defined outside any function are global and can be read from inside functions:

tax_rate = 0.16  # global variable

def calculate_total(price, quantity):
    return price * quantity * (1 + tax_rate)

print(calculate_total(100, 3))  # 348.0
Enter fullscreen mode Exit fullscreen mode

Modifying a global variable inside a function

By default, assigning to a variable inside a function creates a new local variable, even if a global one has the same name. To actually modify the global variable, you need the global keyword:

counter = 0

def increment():
    global counter
    counter += 1

increment()
increment()
print(counter)  # 2
Enter fullscreen mode Exit fullscreen mode

Without global counter, Python would raise an error or create a separate local variable instead of updating the outer one. In general, it's good practice to avoid relying heavily on global state, passing values in as parameters and getting results back via return keeps functions predictable and easier to test.

Putting It All Together

Here's a small example that uses everything above
Parameters with a default value, a return value, and local scope — to clean a simple list of numeric entries:

def clean_numbers(values, default=0):
    """Replace non-numeric entries with a default value and return the cleaned list."""
    cleaned = []  # local variable
    for value in values:
        if isinstance(value, (int, float)):
            cleaned.append(value)
        else:
            cleaned.append(default)
    return cleaned

raw_data = [10, "N/A", 25, None, 30]
result = clean_numbers(raw_data, default=0)
print(result)  # [10, 0, 25, 0, 30]
Enter fullscreen mode Exit fullscreen mode

This function takes a list and an optional default value as parameters, processes the data using a local variable (cleaned), and returns a new list, a pattern you'll use constantly once you start working with real-world, messy data.

Key Takeaways

  • Define a function with def; call it with parentheses to run it.
  • Parameters are placeholders in the definition; arguments are the actual values passed at call time. Defaults, keyword arguments, *args, and **kwargs all give you flexibility in how a function accepts input.
  • return sends a value back to the caller so it can be reused, without it, a function returns None.
  • Scope controls where variables live: local variables exist only inside their function, while global variables are accessible everywhere (though modifying them from inside a function requires the global keyword).

Functions are the basic unit of logic in Python, once you're comfortable defining them, passing data in and out, and knowing where your variables live, you have the building blocks to organize almost any piece of code into something clear and reusable.

Top comments (0)