{ "cells": [ { "cell_type": "markdown", "id": "ab7c01d4", "metadata": { "language": "markdown" }, "source": [ "# Python Basics - 04. Functions\n", "\n", "Functions are reusable blocks of code that make programs cleaner, easier to test, and easier to maintain.\n", "\n", "In this notebook, you will learn:\n", "- how to define and call functions\n", "- how different parameter types work\n", "- how return values are used\n", "- common pitfalls and best practices\n", "- practical advanced patterns like recursion and decorators" ] }, { "cell_type": "markdown", "id": "f7bb1fe8", "metadata": { "language": "markdown" }, "source": [ "## Download Notebook\n", "\n", "{download}`Download this notebook <04_functions.ipynb>`\n" ] }, { "cell_type": "markdown", "id": "bb251edd", "metadata": {}, "source": [ "## 1. Defining and Calling Functions\n", "\n", "Use the `def` keyword to define a function. A function may take input parameters and may return a value." ] }, { "cell_type": "code", "execution_count": null, "id": "62d21646", "metadata": {}, "outputs": [], "source": [ "def greet(name):\n", " # Return a formatted greeting message\n", " return f\"Hello, {name}!\"\n", "\n", "print(greet(\"Python\"))\n", "print(greet(\"Alice\"))" ] }, { "cell_type": "markdown", "id": "e6e9f029", "metadata": {}, "source": [ "## 2. Parameters: Positional, Keyword, and Default Values\n", "\n", "You can pass arguments in several ways:\n", "- positional arguments: matched by position\n", "- keyword arguments: matched by parameter name\n", "- default values: used when an argument is not provided" ] }, { "cell_type": "code", "execution_count": null, "id": "ffd39bc1", "metadata": {}, "outputs": [], "source": [ "def power(base, exponent=2):\n", " # default exponent=2 means square by default\n", " return base ** exponent\n", "\n", "print(power(3)) # positional, uses default exponent\n", "print(power(2, 3)) # positional arguments\n", "print(power(base=4, exponent=2)) # keyword arguments" ] }, { "cell_type": "markdown", "id": "47044bf8", "metadata": {}, "source": [ "## 3. Return Values and Multiple Results\n", "\n", "Functions can return any object, including tuples for multiple values." ] }, { "cell_type": "code", "execution_count": null, "id": "f39e28ad", "metadata": {}, "outputs": [], "source": [ "def divide_and_remainder(a, b):\n", " quotient = a // b\n", " remainder = a % b\n", " return quotient, remainder\n", "\n", "q, r = divide_and_remainder(17, 5)\n", "print(f\"quotient={q}, remainder={r}\")" ] }, { "cell_type": "markdown", "id": "d02c2ed5", "metadata": {}, "source": [ "## 4. Variable Scope (Local vs Global)\n", "\n", "- Local variables exist only inside a function.\n", "- Global variables are defined outside functions.\n", "\n", "Prefer passing values through parameters instead of relying on globals." ] }, { "cell_type": "code", "execution_count": null, "id": "8114ef65", "metadata": {}, "outputs": [], "source": [ "message = \"global\"\n", "\n", "def show_scope():\n", " message = \"local\"\n", " print(\"Inside function:\", message)\n", "\n", "show_scope()\n", "print(\"Outside function:\", message)" ] }, { "cell_type": "markdown", "id": "0ebc1019", "metadata": {}, "source": [ "## 5. Recursion\n", "\n", "A recursive function calls itself with a smaller subproblem.\n", "Every recursive function must have a base case." ] }, { "cell_type": "code", "execution_count": null, "id": "31221ce4", "metadata": {}, "outputs": [], "source": [ "def factorial(n):\n", " if n < 0:\n", " raise ValueError(\"n must be non-negative\")\n", " if n in (0, 1):\n", " return 1\n", " return n * factorial(n - 1)\n", "\n", "print(factorial(5))" ] }, { "cell_type": "markdown", "id": "6d80bb2e", "metadata": {}, "source": [ "## 6. Variable-Length Arguments: `*args` and `**kwargs`\n", "\n", "- `*args` collects positional arguments into a tuple.\n", "- `**kwargs` collects keyword arguments into a dictionary." ] }, { "cell_type": "code", "execution_count": null, "id": "bbd9adc5", "metadata": {}, "outputs": [], "source": [ "def sum_all(*args):\n", " return sum(args)\n", "\n", "def show_profile(**kwargs):\n", " for key, value in kwargs.items():\n", " print(f\"{key}: {value}\")\n", "\n", "print(sum_all(1, 2, 3, 4, 5))\n", "show_profile(name=\"Alice\", role=\"Engineer\", level=\"Beginner\")" ] }, { "cell_type": "markdown", "id": "f16e66b1", "metadata": {}, "source": [ "## 7. Lambda Functions and Decorators (Step by Step)\n", "\n", "### Lambda functions\n", "A `lambda` is a short anonymous function written in one line.\n", "\n", "Use `lambda` when:\n", "- the logic is very small\n", "- you only need the function once\n", "- readability is still clear\n", "\n", "Example pattern:\n", "- regular function: `def square(x): return x * x`\n", "- lambda version: `lambda x: x * x`\n", "\n", "### Decorators\n", "A decorator is a function that takes another function and returns a new function with extra behavior.\n", "\n", "You can use decorators to:\n", "- log function calls\n", "- measure runtime\n", "- check permissions/inputs\n", "- avoid repeating the same pre/post logic in many functions\n", "\n", "Think of it as “wrapping” a function with additional steps before and after it runs." ] }, { "cell_type": "code", "execution_count": null, "id": "101124dc", "metadata": {}, "outputs": [], "source": [ "# ---------- Part A: Lambda (easy comparison) ----------\n", "# Regular function version\n", "def square_def(x):\n", " return x * x\n", "\n", "# Lambda version (same logic, shorter syntax)\n", "square_lambda = lambda x: x * x\n", "\n", "print(\"square_def(6):\", square_def(6))\n", "print(\"square_lambda(6):\", square_lambda(6))\n", "\n", "\n", "# ---------- Part B: Decorator step by step ----------\n", "# Step 1) A normal function\n", "def add(a, b):\n", " return a + b\n", "\n", "print(\"\\nNormal add:\", add(10, 20))\n", "\n", "\n", "# Step 2) A decorator that adds logging around any function\n", "def log_call(func):\n", " def wrapper(*args, **kwargs):\n", " print(f\"[LOG] Calling {func.__name__} with args={args}, kwargs={kwargs}\")\n", " result = func(*args, **kwargs)\n", " print(f\"[LOG] {func.__name__} returned {result}\")\n", " return result\n", " return wrapper\n", "\n", "\n", "# Step 3) Manual wrapping (without @ syntax)\n", "logged_add_manual = log_call(add)\n", "print(\"\\nManual wrapped add:\")\n", "logged_add_manual(3, 5)\n", "\n", "\n", "# Step 4) Decorator syntax sugar (@log_call)\n", "@log_call\n", "def multiply(a, b):\n", " return a * b\n", "\n", "print(\"\\n@decorator wrapped multiply:\")\n", "multiply(4, 6)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }