""" ================================================================================ File: 01_function_basics.py Topic: Python Functions - Basic Concepts ================================================================================ This file demonstrates the fundamentals of functions in Python. Functions are reusable blocks of code that perform specific tasks, making your code more organized, readable, and maintainable. Key Concepts: - Defining functions with def - Calling functions - Function documentation (docstrings) - Variable scope (local vs global) - Basic parameters ================================================================================ """ # ----------------------------------------------------------------------------- # 1. Defining and Calling Functions # ----------------------------------------------------------------------------- # Use 'def' keyword to define a function print("--- Defining and Calling Functions ---") # Simple function with no parameters def greet(): """Print a greeting message.""" print("Hello, World!") # Call the function greet() # Function with a parameter def greet_person(name): """Greet a specific person.""" print(f"Hello, {name}!") greet_person("Baraa") greet_person("Sara") # ----------------------------------------------------------------------------- # 2. Function with Multiple Parameters # ----------------------------------------------------------------------------- print("\n--- Multiple Parameters ---") def introduce(name, age, city): """Introduce a person with their details.""" print(f"My name is {name}, I am {age} years old, and I live in {city}.") introduce("Ali", 25, "Cairo") # ----------------------------------------------------------------------------- # 3. Return Values # ----------------------------------------------------------------------------- # Functions can return values using 'return' print("\n--- Return Values ---") def add(a, b): """Add two numbers and return the result.""" return a + b result = add(5, 3) print(f"5 + 3 = {result}") # Using returned value in expressions total = add(10, 20) + add(5, 5) print(f"(10+20) + (5+5) = {total}") # Function without explicit return returns None def print_message(msg): print(msg) result = print_message("Hello") print(f"Function without return: {result}") # None # Early return def get_grade(score): """Return letter grade based on score.""" if score >= 90: return "A" if score >= 80: return "B" if score >= 70: return "C" if score >= 60: return "D" return "F" print(f"\nScore 85 → Grade {get_grade(85)}") # ----------------------------------------------------------------------------- # 4. Docstrings - Function Documentation # ----------------------------------------------------------------------------- # Use triple quotes to document what a function does print("\n--- Docstrings ---") def calculate_area(length, width): """ Calculate the area of a rectangle. Args: length: The length of the rectangle (positive number) width: The width of the rectangle (positive number) Returns: The area of the rectangle (length * width) Example: >>> calculate_area(5, 3) 15 """ return length * width # Access docstring print(f"Function docstring:\n{calculate_area.__doc__}") # Using help() # help(calculate_area) # Uncomment to see full help # ----------------------------------------------------------------------------- # 5. Variable Scope - Local vs Global # ----------------------------------------------------------------------------- # Variables inside functions are local by default print("\n--- Variable Scope ---") global_var = "I am global" def demonstrate_scope(): """Demonstrate variable scope.""" local_var = "I am local" print(f" Inside function - global_var: {global_var}") print(f" Inside function - local_var: {local_var}") demonstrate_scope() print(f"Outside function - global_var: {global_var}") # print(local_var) # This would cause an error! # Modifying global variables inside functions counter = 0 def increment_counter(): """Increment the global counter.""" global counter # Declare we want to modify global variable counter += 1 print(f" Counter inside function: {counter}") print(f"\nCounter before: {counter}") increment_counter() increment_counter() print(f"Counter after: {counter}") # Local variable shadows global value = 100 def shadow_example(): """Local variable shadows global.""" value = 200 # This is a new local variable, not the global one print(f" Inside function: {value}") print(f"\nGlobal value: {value}") shadow_example() print(f"Global value unchanged: {value}") # ----------------------------------------------------------------------------- # 6. Multiple Return Values # ----------------------------------------------------------------------------- # Functions can return multiple values as a tuple print("\n--- Multiple Return Values ---") def get_min_max(numbers): """Return both minimum and maximum of a list.""" return min(numbers), max(numbers) data = [5, 2, 8, 1, 9, 3] minimum, maximum = get_min_max(data) print(f"List: {data}") print(f"Min: {minimum}, Max: {maximum}") # Return multiple named values using dictionary def analyze_text(text): """Analyze text and return statistics.""" return { "length": len(text), "words": len(text.split()), "uppercase": sum(1 for c in text if c.isupper()) } stats = analyze_text("Hello World! How Are You?") print(f"\nText stats: {stats}") # ----------------------------------------------------------------------------- # 7. Pass Statement - Placeholder Functions # ----------------------------------------------------------------------------- # Use pass to create empty function bodies print("\n--- Placeholder Functions ---") def future_feature(): """This will be implemented later.""" pass # Placeholder - does nothing def another_placeholder(): """Placeholder with ellipsis (also valid).""" ... # Alternative to pass future_feature() # Can be called, just does nothing print("Placeholder functions work!") # ----------------------------------------------------------------------------- # 8. Nested Functions # ----------------------------------------------------------------------------- # Functions can be defined inside other functions print("\n--- Nested Functions ---") def outer_function(message): """Outer function that contains an inner function.""" def inner_function(): """Inner function that uses outer's variable.""" print(f" Inner says: {message}") print("Outer function called") inner_function() outer_function("Hello from outer!") # Inner function not accessible outside # inner_function() # This would cause an error! # Practical example: Helper function def calculate_statistics(numbers): """Calculate various statistics using helper functions.""" def mean(nums): return sum(nums) / len(nums) def variance(nums): avg = mean(nums) return sum((x - avg) ** 2 for x in nums) / len(nums) return { "mean": mean(numbers), "variance": variance(numbers), "std_dev": variance(numbers) ** 0.5 } data = [2, 4, 4, 4, 5, 5, 7, 9] stats = calculate_statistics(data) print(f"\nStatistics for {data}:") for key, value in stats.items(): print(f" {key}: {value:.2f}") # ----------------------------------------------------------------------------- # 9. Functions as Objects # ----------------------------------------------------------------------------- # In Python, functions are first-class objects print("\n--- Functions as Objects ---") def say_hello(name): """Just say hello.""" return f"Hello, {name}!" # Assign function to variable greeting_func = say_hello print(greeting_func("World")) # Store functions in a list def add_one(x): return x + 1 def double(x): return x * 2 def square(x): return x * x operations = [add_one, double, square] value = 5 print(f"\nApplying operations to {value}:") for func in operations: print(f" {func.__name__}({value}) = {func(value)}") # Pass function as argument def apply_operation(func, value): """Apply a function to a value.""" return func(value) print(f"\napply_operation(double, 10) = {apply_operation(double, 10)}") # ----------------------------------------------------------------------------- # 10. Practical Examples # ----------------------------------------------------------------------------- print("\n--- Practical Examples ---") # Temperature converter def celsius_to_fahrenheit(celsius): """Convert Celsius to Fahrenheit.""" return (celsius * 9/5) + 32 def fahrenheit_to_celsius(fahrenheit): """Convert Fahrenheit to Celsius.""" return (fahrenheit - 32) * 5/9 print(f"20°C = {celsius_to_fahrenheit(20):.1f}°F") print(f"68°F = {fahrenheit_to_celsius(68):.1f}°C") # Password validator def is_valid_password(password): """ Check if password meets requirements: - At least 8 characters - Contains uppercase letter - Contains lowercase letter - Contains digit """ if len(password) < 8: return False, "Too short" if not any(c.isupper() for c in password): return False, "No uppercase letter" if not any(c.islower() for c in password): return False, "No lowercase letter" if not any(c.isdigit() for c in password): return False, "No digit" return True, "Valid password" test_passwords = ["short", "alllowercase", "ALLUPPERCASE", "ValidPass123"] print("\nPassword validation:") for pwd in test_passwords: is_valid, message = is_valid_password(pwd) print(f" '{pwd}': {message}")