Part 6 of a beginner-friendly series on learning Python from scratch.
In Part 5, we learned to organize data with lists, dictionaries, and other collections. Now it’s time to organize our code itself.
A function is a reusable block of code that performs a specific task. Instead of writing the same code multiple times, you write it once in a function, then call that function whenever you need it. This is the foundation of writing clean, maintainable programs.
Defining and Calling Functions
The basics
def greet():
print("Hello, World!")
greet() # Call the function
Enter fullscreen mode Exit fullscreen mode
Use def to define a function. The function name is followed by parentheses and a colon. The indented block below is the function’s body.
When you call the function (by typing its name with parentheses), Python runs the code inside it.
Functions with parameters
Most functions need information to work with. That’s what parameters are for:
def greet(name):
print(f"Hello, {name}!")
greet("Ramesh") # Hello, Ramesh!
greet("Priya") # Hello, Priya!
Enter fullscreen mode Exit fullscreen mode
name is a parameter (placeholder). When you call greet("Ramesh"), name becomes "Ramesh" inside the function.
Multiple parameters:
def add(x, y):
print(x + y)
add(5, 3) # 8
add(10, 20) # 30
Enter fullscreen mode Exit fullscreen mode
Return values
A function can calculate something and give the result back to you with return:
def add(x, y):
return x + y
result = add(5, 3)
print(result) # 8
Enter fullscreen mode Exit fullscreen mode
The return statement stops the function and sends a value back. The caller can then use that value.
def greet(name):
message = f"Hello, {name}!"
return message
greeting = greet("Ramesh")
print(greeting) # Hello, Ramesh!
Enter fullscreen mode Exit fullscreen mode
A function can return multiple values as a tuple:
def get_user_info():
return "Ramesh", 25, "Chennai"
name, age, city = get_user_info()
print(name, age, city) # Ramesh 25 Chennai
Enter fullscreen mode Exit fullscreen mode
Arguments: Positional vs Keyword
There are two ways to pass values to a function:
Positional arguments
Arguments are matched by position:
def describe(name, age):
print(f"{name} is {age} years old")
describe("Ramesh", 25) # Ramesh is 25 years old
describe(25, "Ramesh") # 25 is Ramesh years old (wrong order, confusing)
Enter fullscreen mode Exit fullscreen mode
Order matters.
Keyword arguments
You can use parameter names to be explicit:
def describe(name, age):
print(f"{name} is {age} years old")
describe(age=25, name="Ramesh") # Ramesh is 25 years old
describe(name="Ramesh", age=25) # Ramesh is 25 years old (order doesn't matter)
Enter fullscreen mode Exit fullscreen mode
Keyword arguments make code clearer and are less error-prone. Mix them freely:
describe("Ramesh", age=25) # First positional, then keyword
Enter fullscreen mode Exit fullscreen mode
Default Arguments
You can give parameters default values — they’re optional:
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Ramesh")) # Hello, Ramesh!
print(greet("Ramesh", "Hi")) # Hi, Ramesh!
print(greet("Ramesh", greeting="Hey")) # Hey, Ramesh!
Enter fullscreen mode Exit fullscreen mode
If the caller doesn’t provide greeting, it defaults to "Hello".
Important rule: Parameters with defaults must come after parameters without defaults:
# Correct
def add(x, y, verbose=False):
result = x + y
if verbose:
print(f"{x} + {y} = {result}")
return result
# Wrong — SyntaxError
def add(x=0, y):
return x + y
Enter fullscreen mode Exit fullscreen mode
*args — Variable Number of Arguments
Sometimes you want a function to accept any number of arguments. Use *args:
def add_many(*numbers):
total = 0
for num in numbers:
total += num
return total
print(add_many(1, 2, 3)) # 6
print(add_many(1, 2, 3, 4, 5)) # 15
print(add_many()) # 0 (no arguments)
Enter fullscreen mode Exit fullscreen mode
*args (the name args is conventional) collects all extra arguments into a tuple. The * unpacks them.
def print_all(*items):
for item in items:
print(item)
print_all("apple", "banana", "cherry", 42)
# Prints each on its own line
Enter fullscreen mode Exit fullscreen mode
**kwargs — Keyword Arguments as Dictionary
Similar to *args, use **kwargs to accept any number of keyword arguments:
def make_profile(**info):
for key, value in info.items():
print(f"{key}: {value}")
make_profile(name="Ramesh", age=25, city="Chennai")
# Output:
# name: Ramesh
# age: 25
# city: Chennai
Enter fullscreen mode Exit fullscreen mode
**kwargs (convention) collects keyword arguments into a dictionary.
Combining everything
You can use *args and **kwargs together:
def my_function(required, *args, default_arg="default", **kwargs):
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Default: {default_arg}")
print(f"Kwargs: {kwargs}")
my_function(1, 2, 3, 4, default_arg="custom", extra1="value1", extra2="value2")
# Output:
# Required: 1
# Args: (2, 3, 4)
# Default: custom
# Kwargs: {'extra1': 'value1', 'extra2': 'value2'}
Enter fullscreen mode Exit fullscreen mode
Order: positional, *args, keyword defaults, **kwargs.
Scope and Variable Lifetime
Variables have scope — the region of code where they exist and are accessible.
Local scope
Variables created inside a function only exist inside that function:
def greet():
message = "Hello" # Local variable
print(message)
greet() # Hello
print(message) # NameError — message doesn't exist here
Enter fullscreen mode Exit fullscreen mode
Global scope
Variables created outside functions are global:
message = "Hello" # Global variable
def greet():
print(message) # Can access global variables
greet() # Hello
print(message) # Hello
Enter fullscreen mode Exit fullscreen mode
Modifying global variables
Be careful — inside a function, assignment creates a local variable:
count = 0
def increment():
count = count + 1 # UnboundLocalError — trying to use local before assignment
return count
increment()
Enter fullscreen mode Exit fullscreen mode
To modify a global variable inside a function, use global:
count = 0
def increment():
global count
count = count + 1
return count
increment()
print(count) # 1
Enter fullscreen mode Exit fullscreen mode
Tip: Generally, avoid modifying global variables. It makes code hard to understand. Instead, pass values in and return values out.
Lambda Functions — Quick Throwaway Functions
A lambda is a tiny anonymous function, useful for one-liners:
square = lambda x: x ** 2
print(square(5)) # 25
Enter fullscreen mode Exit fullscreen mode
Syntax: lambda parameters: expression
Lambda functions are usually passed to other functions. For example, sorted() can take a function that determines the sort order:
people = [
{"name": "Ramesh", "age": 25},
{"name": "Priya", "age": 20},
{"name": "Arun", "age": 30}
]
# Sort by age
sorted_by_age = sorted(people, key=lambda person: person["age"])
print(sorted_by_age)
# [{'name': 'Priya', 'age': 20}, {'name': 'Ramesh', 'age': 25}, {'name': 'Arun', 'age': 30}]
Enter fullscreen mode Exit fullscreen mode
Or with map() to transform a list:
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
Enter fullscreen mode Exit fullscreen mode
Lambda is elegant for simple operations. For anything complex, write a proper function.
Recursion — Functions Calling Themselves
A recursive function calls itself. It’s powerful for solving problems that repeat the same pattern at smaller scales.
Every recursive function needs a base case (when to stop) and a recursive case (how to shrink the problem):
def countdown(n):
# Base case
if n == 0:
print("Blastoff!")
return
# Recursive case
print(n)
countdown(n - 1)
countdown(5)
# Output:
# 5
# 4
# 3
# 2
# 1
# Blastoff!
Enter fullscreen mode Exit fullscreen mode
Without a base case, the function recurses infinitely and crashes:
def bad_countdown(n):
print(n)
bad_countdown(n - 1) # Never stops!
bad_countdown(5) # RecursionError: maximum recursion depth exceeded
Enter fullscreen mode Exit fullscreen mode
Classic example: Factorial
def factorial(n):
# Base case
if n == 0 or n == 1:
return 1
# Recursive case
return n * factorial(n - 1)
print(factorial(5)) # 120 (5 * 4 * 3 * 2 * 1)
Enter fullscreen mode Exit fullscreen mode
Recursion is elegant but can be slow and memory-intensive. Use it when the problem naturally breaks into recursive patterns (like tree structures), not for simple loops.
Generators and Iterators
A generator is a function that yields values one at a time, instead of returning them all at once. Use yield:
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for num in count_up_to(5):
print(num)
# Output:
# 1
# 2
# 3
# 4
# 5
Enter fullscreen mode Exit fullscreen mode
The function pauses at yield and resumes where it left off. This is memory-efficient for large datasets:
# Without generator — creates entire list in memory
def range_list(n):
result = []
for i in range(n):
result.append(i)
return result
# With generator — creates values on-demand
def range_gen(n):
for i in range(n):
yield i
# Both work with for loops, but generator is more efficient
for num in range_list(1000000): # Creates 1 million item list
print(num)
for num in range_gen(1000000): # Creates values as needed
print(num)
Enter fullscreen mode Exit fullscreen mode
Generators are a more advanced topic — understand them conceptually now; you’ll use them more as you progress.
Practical Examples
Example 1: Simple calculator
def add(x, y):
return x + y
def subtract(x, y):
return x - y
def multiply(x, y):
return x * y
def divide(x, y):
if y == 0:
return "Cannot divide by zero"
return x / y
print(add(10, 5)) # 15
print(divide(10, 0)) # Cannot divide by zero
Enter fullscreen mode Exit fullscreen mode
Example 2: Processing collections
def find_max(numbers):
if len(numbers) == 0:
return None
max_num = numbers[0]
for num in numbers:
if num > max_num:
max_num = num
return max_num
print(find_max([3, 7, 2, 9, 1])) # 9
Enter fullscreen mode Exit fullscreen mode
Example 3: Flexible greeting
def create_greeting(*names, greeting="Hello", punctuation="!"):
message = f"{greeting}, {', '.join(names)}{punctuation}"
return message
print(create_greeting("Ramesh", "Priya"))
# Hello, Ramesh, Priya!
print(create_greeting("Alice", "Bob", greeting="Hi", punctuation="?"))
# Hi, Alice, Bob?
Enter fullscreen mode Exit fullscreen mode
Why This Matters
Functions are how you stop repeating yourself. Instead of copying the same code 10 times, you write it once, call it 10 times. This saves time, prevents bugs, and makes changes easier.
The most common beginner mistakes:
- Forgetting parentheses when calling a function
- Using
returnto print (they’re different — one sends a value back, one displays it) - Trying to use variables defined inside a function outside of it
- Infinite recursion (forgetting the base case)
- Confusing
*argswith*unpacking operator
What’s Next
In Part 7, we explored with modules, errors, and files — using other people’s code, handling problems gracefully, and reading/writing data to disk. Functions you write will often read or write files.
This is Part 6 of an 8-part beginner Python series. Catch up on Part 1: Getting Started & Syntax, Part 2: Variables, Data Types & Numbers, Part 3: Strings & Booleans, Part 4: Operators & Control Flow, and Part 5: Collections.
답글 남기기