Python Functions
Functions in Python are blocks of reusable code that perform a specific task. They help organize code, reduce repetition, and improve readability.
---1. Defining a Function
Use the def keyword followed by the function name and parentheses. The body of the function is indented.
def greet():
print("Hello, World!")
greet() # Calling the function
---
2. Function Parameters
Functions can accept inputs (parameters) to work with dynamic data.
def greet_user(name):
print(f"Hello, {name}!")
greet_user("Talha")
2.1 Default Parameters
def greet_user(name="Guest"):
print(f"Hello, {name}!")
greet_user() # Hello, Guest!
greet_user("Ali") # Hello, Ali!
2.2 Keyword Arguments
You can specify arguments by name, in any order:
def describe_pet(name, species):
print(f"{name} is a {species}")
describe_pet(species="dog", name="Buddy")
2.3 Variable-length Arguments
Python allows you to define functions that can accept a variable number of arguments. This is useful when you don't know in advance how many arguments might be passed.
*args→ used to pass multiple positional arguments as a tuple.**kwargs→ used to pass multiple keyword arguments as a dictionary.
Using *args (Positional Arguments)
def sum_all(*numbers):
"""
Accepts any number of numbers and returns their sum.
*numbers collects all positional arguments as a tuple.
"""
print("Numbers received:", numbers)
return sum(numbers)
# Calling with different numbers of arguments
print(sum_all(1, 2, 3)) # Output: Numbers received: (1, 2, 3) → 6
print(sum_all(5, 10, 15, 20)) # Output: Numbers received: (5, 10, 15, 20) → 50
Using **kwargs (Keyword Arguments)
def print_info(**info):
"""
Accepts any number of keyword arguments.
**info collects them into a dictionary.
"""
print("Info dictionary:", info)
for key, value in info.items():
print(f"{key}: {value}")
# Calling with different keyword arguments
print_info(name="Talha", age=25, city="Karachi")
# Output:
# Info dictionary: {'name': 'Talha', 'age': 25, 'city': 'Karachi'}
# name: Talha
# age: 25
# city: Karachi
Combining *args and **kwargs
You can combine both to accept arbitrary positional and keyword arguments:
def display(*args, **kwargs):
print("Positional args:", args)
print("Keyword args:", kwargs)
display(1, 2, 3, name="Talha", age=25)
# Output:
# Positional args: (1, 2, 3)
# Keyword args: {'name': 'Talha', 'age': 25}
Unpacking Arguments
You can also unpack lists/tuples and dictionaries into functions using * and **:
nums = [1, 2, 3, 4]
print(sum_all(*nums)) # Equivalent to sum_all(1,2,3,4) → 10
info = {"name": "Talha", "age": 25}
print_info(**info)
# Equivalent to print_info(name="Talha", age=25)
Key Points:
*argsmust appear before**kwargsin the function definition.- You can combine regular parameters with
*argsand**kwargs:
def example(a, b, *args, **kwargs):
print(a, b)
print("Additional args:", args)
print("Additional kwargs:", kwargs)
example(1, 2, 3, 4, name="Talha", city="Karachi")
# Output:
# 1 2
# Additional args: (3, 4)
# Additional kwargs: {'name': 'Talha', 'city': 'Karachi'}
3. Return Values
Functions can return values using the return keyword. Without return, a Python function returns None by default. You can also optionally use type hints to indicate the expected return type.
Basic Example
def add(a: int, b: int) -> int:
"""Returns the sum of two numbers"""
return a + b
result = add(5, 10)
print(result) # Output: 15
Returning Multiple Values
You can return multiple values as a tuple, and type hints can specify this:
from typing import Tuple
def get_user_info() -> Tuple[str, int]:
name = "Talha"
age = 25
return name, age
user_name, user_age = get_user_info()
print(user_name) # Talha
print(user_age) # 25
Returning Collections
Functions can return lists, tuples, dictionaries, etc. Type hints make this explicit:
from typing import List, Tuple
# Returning a list of integers
def get_numbers() -> List[int]:
return [1, 2, 3, 4, 5]
numbers = get_numbers()
print(numbers) # [1, 2, 3, 4, 5]
# Returning a tuple of integers
def get_numbers_tuple() -> Tuple[int, ...]:
return (1, 2, 3, 4, 5)
numbers_tuple = get_numbers_tuple()
print(numbers_tuple) # (1, 2, 3, 4, 5)
Key Notes
- Functions without
returnreturnNoneby default. - You can return any Python object: numbers, strings, lists, dictionaries, tuples, or even functions.
- Returning multiple values actually returns a tuple.
- Type hints (
-> int,-> List[int], etc.) improve readability and help IDEs, but are not enforced at runtime.
4. Lambda Functions (Anonymous Functions)
Lambda functions are small, one-line anonymous functions that are often used for short tasks. They are defined using the lambda keyword.
Basic Example
square = lambda x: x**2
print(square(5)) # 25
Lambda with Multiple Arguments
add = lambda a, b: a + b
print(add(3, 4)) # 7
Lambda in Functions
Can be used as a quick callback or in functions like map(), filter(), and sorted():
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # [2, 4]
Key Notes
- Lambda functions are limited to a single expression.
- They are useful for short, throwaway functions.
- Cannot contain statements, only expressions.
5. Scope of Variables in Functions
Python uses the LEGB rule to determine variable scope:
- Local – inside the function
- Enclosing – inside an enclosing function
- Global – outside all functions
- Built-in – Python built-in names
Example of Local vs Global
x = 10 # Global variable
def func():
x = 5 # Local variable
print("Inside function:", x)
func() # Inside function: 5
print("Outside function:", x) # Outside function: 10
Using the Global Keyword
Modify a global variable inside a function:
x = 10
def update():
global x
x = 20
update()
print(x) # 20
Enclosing Scope Example (Nested Functions)
def outer():
x = "Outer Variable"
def inner():
print("Inner sees:", x) # Accesses enclosing scope variable
inner()
outer() # Inner sees: Outer Variable
Key Notes
- Local variables exist only inside their function.
- Global variables can be accessed inside functions but need
globalto modify. - Enclosing variables are available to nested functions (closures).
6. Nested Functions
Functions can be defined inside other functions. The inner function can access variables from the enclosing function, creating closures.
Basic Example
def outer():
x = "Hello"
def inner():
print(x) # Accesses outer function variable
inner()
outer() # Hello
Returning a Nested Function
Nested functions can also be returned from their enclosing function:
def outer_func(msg):
def inner_func():
print(msg)
return inner_func
func = outer_func("Hi Talha!")
func() # Hi Talha!
Use Cases of Nested Functions
- Encapsulation – keeping helper functions private.
- Closures – preserving state between function calls.
- Decorators – modifying functions dynamically.
7. Best Practices
- Use meaningful function names
- Keep functions small and focused on one task
- Use docstrings to describe function behavior
- Use default arguments and keyword arguments for clarity
8. Interview Questions
Q1. What is a function in Python?
A block of code that performs a specific task and can be reused.
Q2. Difference between local and global variable in a function?
Local variables exist inside a function, global variables exist outside and can be accessed anywhere.
Q3. What is a lambda function?
A small anonymous function defined using the lambda keyword, usually for short operations.
Q4. Can functions return multiple values?
Yes, by returning a tuple: return a, b, c