Functions - Creating Your Own Commands

Functions are like kitchen appliances. A blender (function) takes ingredients (inputs), processes them, and gives you a smoothie (output). You don't need to know how the blender works inside - just what goes in and what comes out.
A **function** is a reusable block of code that performs a specific task. Instead of writing the same code multiple times, you define a function once and then call it whenever you need that behavior. This makes your programs shorter, more organized, and easier to debug.

**Defining a function:**
Use the `def` keyword, followed by the function name, parentheses `()`, and a colon `:`.
```python
def function_name():
# code block
```

The function name should be descriptive (verb or verb phrase) and follow the same naming rules as variables (lowercase with underscores).

**Calling a function:**
Write the function name followed by parentheses:
```python
function_name()
```

**Parameters and arguments:**
- **Parameters** are variables listed in the function definition. They act as placeholders for the values the function will receive.
- **Arguments** are the actual values you pass to the function when calling it.
```python
def greet(name): # 'name' is a parameter
print(f"Hello {name}")

greet("Alice") # 'Alice' is an argument
```

**Return values:**
Use the `return` statement to send a value back to the caller. If no `return` is used, the function returns `None`.
```python
def add(a, b):
return a + b

result = add(5, 3) # result becomes 8
```

**Default parameter values:**
You can provide default values for parameters. If the caller doesn't supply an argument, the default is used.
```python
def greet(name="Guest"):
print(f"Hello {name}")

greet() # Hello Guest
greet("Sam") # Hello Sam
```

**Keyword arguments:**
When calling a function, you can specify arguments by parameter name, which allows you to pass them in any order:
```python
def describe_person(name, age, city):
print(f"{name} is {age} and lives in {city}")

describe_person(age=25, city="New York", name="John")
```

**Variable scope:**
- Variables defined inside a function are **local** – they only exist inside that function.
- Variables defined outside any function are **global** – they can be accessed anywhere (but modifying them inside a function requires the `global` keyword).

**Docstrings (documentation):**
A docstring is a string literal right after the function definition that explains what the function does. It's enclosed in triple quotes `"""`.
```python
def multiply(a, b):
"""Return the product of a and b."""
return a * b
```
You can view a function's docstring with `help(multiply)`.

**Why use functions?**
1. **Reusability** – write once, use many times.
2. **Modularity** – break complex problems into smaller, manageable pieces.
3. **Abstraction** – hide complex implementation details.
4. **Testing** – easier to test individual functions.
5. **Collaboration** – different developers can work on different functions.

**Common mistakes:**
- Forgetting the colon `:` after the function definition.
- Indentation errors – all code inside the function must be indented.
- Using a variable name that shadows a built‑in function (e.g., `def print():`).
- Forgetting to return a value when needed (returns `None` by default).
- Modifying a global variable without declaring `global` inside the function.

**Practice exercises:**
1. Write a function `is_even(n)` that returns `True` if `n` is even, `False` otherwise.
2. Write a function `factorial(n)` that returns the factorial of `n` (e.g., `5! = 120`).
3. Write a function `greet_user(first_name, last_name)` that returns a full greeting.
4. Write a function `max_of_three(a, b, c)` that returns the largest number.
5. Write a function `count_vowels(text)` that returns the number of vowels in a string.

**Real‑world applications:**
- Mathematical calculations (e.g., `calculate_tax(amount, rate)`)
- Data validation (e.g., `is_valid_email(email)`)
- File processing (e.g., `read_csv(filename)`)
- API wrappers (e.g., `get_weather(city)`)
- Game logic (e.g., `calculate_damage(attack, defense)`)

**Example with multiple return values:**
Functions can return multiple values as a tuple:
```python
def get_min_max(numbers):
return min(numbers), max(numbers)

smallest, largest = get_min_max([5, 2, 8, 1, 9])
```
# ========== EXAMPLE 1: Basic Function (No Parameters, No Return) ==========
print("=== Example 1: Simple Greeting Function ===")
def say_hello():
    """This function prints a welcome message"""
    print("Hello!")
    print("Welcome to Python!")

print("Calling function first time:")
say_hello()
print("\nCalling function second time:")
say_hello()
print()

# ========== EXAMPLE 2: Function with Parameters and Return Value ==========
print("=== Example 2: Area Calculator and Temperature Converter ===")
def calculate_area(length, width):
    """Calculate area of a rectangle"""
    area = length * width
    return area

room_area = calculate_area(10, 12)
print(f"Room area: {room_area} square feet")

def celsius_to_fahrenheit(celsius):
    """Convert Celsius to Fahrenheit"""
    return (celsius * 9/5) + 32

temp_c = 25
temp_f = celsius_to_fahrenheit(temp_c)
print(f"{temp_c}°C = {temp_f}°F")
print()

# ========== EXAMPLE 3: Default Parameters and Keyword Arguments ==========
print("=== Example 3: Default Values and Keyword Arguments ===")
def order_coffee(flavor="regular", size="medium"):
    """Order a coffee with optional flavor and size"""
    print(f"One {size} {flavor} coffee coming up!")

# Using default parameters
order_coffee()                         # both defaults
order_coffee("cappuccino")            # override flavor, size default
order_coffee(size="large")            # keyword argument
order_coffee(flavor="vanilla", size="small")  # both keyword arguments

# Function with multiple return values
def get_stats(numbers):
    """Return min and max of a list as a tuple"""
    return min(numbers), max(numbers)

scores = [85, 92, 78, 90, 88]
lowest, highest = get_stats(scores)
print(f"\nScores: {scores}")
print(f"Lowest: {lowest}, Highest: {highest}")

→ Run this code interactively