Loading notes...
Loading notes...
Class 12 • Chapter 2
Functions allow us to write modular, reusable, and clean code by grouping logic into named blocks.
In programming, modularity is the practice of breaking a large, complex problem into smaller, independent, and manageable sub-tasks. Functions are the primary tool for achieving this. Instead of writing a single thousand-line script, a developer writes discrete functions—each responsible for one specific task. This approach follows the DRY (Don't Repeat Yourself) principle. By isolating logic into functions, you reduce code redundancy, make debugging significantly easier (as you only need to fix a bug in one place), and improve the overall readability of your project. Think of functions as 'black boxes': you provide them with specific inputs, and they perform a transformation to produce a predictable output without the caller needing to know the internal implementation details.
In Python, a function is defined using the 'def' keyword, followed by a unique function name and a set of parentheses containing any parameters. The header ends with a colon (:), and the body must be indented. A function only executes when it is 'called' by its name followed by parentheses. Parameters are the variables listed in the function definition, acts as placeholders for the data the function will receive. Arguments, on the other hand, are the actual values passed to the function during a call. If a function is defined with two parameters, it generally expects two arguments during the call, though Python provides several flexible ways to handle inputs.
def greet(name):
"""This function greets the person passed in."""
print("Hello, " + name + "! Welcome to PyLearn.")
# Calling the function
greet("Basant")Python is exceptionally flexible with how it handles data passed to functions. **Positional Arguments** are matched based on their order in the function call. **Keyword Arguments** allow you to pass values by explicitly naming the parameter, which means the order no longer matters. **Default Parameters** permit you to define a 'fallback' value if an argument is not provided, making the function more versatile. For advanced scenarios, Python uses **Variable-Length Arguments**: '*args' allows a function to receive any number of positional arguments as a tuple, while '**kwargs' allows it to receive any number of keyword arguments as a dictionary. Mastering these allows for the creation of highly adaptable APIs.
def person_info(name, age=18, *hobbies, **details):
print(f"Name: {name}, Age: {age}")
print(f"Hobbies: {hobbies}")
print(f"Extra Details: {details}")
# Calling with various argument types
person_info("Alice", 25, "Coding", "Hiking", city="London", status="Active")The 'return' statement is used to exit a function and send a result back to the caller. Unlike 'print()', which simply displays a value, 'return' gives the data back to the program so it can be stored in a variable or used in further calculations. If a function doesn't explicitly include a return statement, it implicitly returns 'None'. A powerful feature in Python is the ability to return multiple values separated by commas; Python automatically packs these into a 'Tuple,' allowing a single function to provide multiple pieces of related data simultaneously.
def get_stats(numbers):
low = min(numbers)
high = max(numbers)
avg = sum(numbers) / len(numbers)
return low, high, avg # Returns a tuple
minimum, maximum, average = get_stats([10, 20, 30, 40, 50])
print(f"Min: {minimum}, Max: {maximum}")Scope refers to where a variable can be seen and accessed within your code. Python follows the LEGB rule for searching variable names: **Local** (inside the function), **Enclosing** (in nested functions), **Global** (at the module level), and **Built-in** (Python's internal names). A variable created inside a function is 'Local' and cannot be accessed outside it. Conversely, a function can read a 'Global' variable but cannot modify it directly unless the 'global' keyword is used. This isolation is crucial for protecting data and preventing accidental side effects where one part of a program unexpectedly changes data used by another.
count = 0
def increment():
global count # Allows modification of global variable
count += 1
increment()
print(count) # Output: 1Lambda functions are small, restricted, anonymous functions that can be defined in a single line without a name using the 'lambda' keyword. They follow the syntax: 'lambda arguments : expression'. While they are limited because they can only contain one expression (and no statements like return or for-loops), they are incredibly useful for short-lived tasks, especially when passed as arguments to higher-order functions like map(), filter(), or sort(). They allow for cleaner, 'functional' style code when the logic is simple enough that defining a full formal function would be overkill.
# Sort list of tuples by the second element
points = [(1, 9), (4, 2), (3, 5)]
points.sort(key=lambda x: x[1])
print(points) # Output: [(4, 2), (3, 5), (1, 9)]Recursion is a programming technique where a function calls itself to solve smaller instances of the same problem. For a recursive function to be successful, it MUST have two parts: a **Base Case** (a simple condition that stops the recursion) and a **Recursive Step** (where the function calls itself with a modified input that moves closer to the base case). While powerful for problems involving trees or nested structures, recursion must be used carefully; if a base case is missing or unreachable, the function will call itself infinitely until the computer runs out of memory, resulting in a 'RecursionError' (Stack Overflow).
def factorial(n):
if n == 1: # Base Case
return 1
else:
return n * factorial(n - 1) # Recursive Step
print(factorial(5)) # Output: 120A Docstring (Documentation String) is a literal string placed as the first statement in a function, usually enclosed in triple quotes ("""). Unlike regular comments, docstrings are stored as an attribute of the function (`__doc__`) and can be accessed at runtime using the help() function or by IDEs to provide tooltips. Professional code always includes docstrings to explain what the function does, what its parameters expect, and what it returns. This 'self-documenting' nature makes codebases significantly easier to maintain and share with other developers.
def square(n):
"""Returns the square of a number."""
return n**2
print(square.__doc__)
# help(square)