{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Functions\n",
    "\n",
    "We use functions in programming to bundle a set of instructions in a self-contained package. A function is a piece of code written to carry out a specific task. Functions are useful when we want to execute a specific task multiple times within our program.\n",
    "\n",
    "A function typically has two main components: \n",
    "\n",
    "1. an **input**, which can be assigned a default if not specified\n",
    "2. an **output**, which gets return once the code inside the function is finished running \n",
    "\n",
    "<img width='70%' src=\"https://practicalpython.s3.us-east-2.amazonaws.com/assets/function_diagram.png\"/>\n",
    "\n",
    "The general structure of a function looks like this:\n",
    "\n",
    "```\n",
    "def task_name(input):\n",
    "    # task code goes here\n",
    "    return output \n",
    "```\n",
    "\n",
    "The input(s) of the function are called `parameters`. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Built-in Python Functions\n",
    "\n",
    "Python comes with a wide variety of built-in functions. For example, `type()` is a function that takes a value or variable name as its input and returns the name of the datatype as the output. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "int"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`sum()` is another built-in Python function that takes in a list of values and returns the sum. Similarly, `len()` takes in a list and returns the length of that list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sum: 60\n",
      "len: 3\n"
     ]
    }
   ],
   "source": [
    "n_apples = [10,20,30]\n",
    "\n",
    "print(f\"sum: {sum(n_apples)}\")\n",
    "print(f\"len: {len(n_apples)}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want to learn more about a Python function, you can use the `help()` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function sum in module builtins:\n",
      "\n",
      "sum(iterable, /, start=0)\n",
      "    Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n",
      "    \n",
      "    When the iterable is empty, return the start value.\n",
      "    This function is intended specifically for use with numeric values and may\n",
      "    reject non-numeric types.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(sum)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How to Define Your Own Function\n",
    "\n",
    "When defining your own function, here are the steps that you should follow:\n",
    "\n",
    "1. Use the keyword `def` to declare the function and follow this with the function name\n",
    "2. Add parameters (inputs) to the function. These go inside the parentheses of the function.\n",
    "3. Add statements (instructions/logic) that the function should execute. \n",
    "4. End the function with a return statement so that it returns the desired output.\n",
    "\n",
    "You don't need to have a return statement for your function to be valid. What happens when your function doesn't return anything?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello!\n"
     ]
    }
   ],
   "source": [
    "def hello():\n",
    "    print(\"hello!\")\n",
    "\n",
    "hello()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Don't be confused with the function above! It's printing `\"hello!\"` but it's not returning it. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello!\n",
      "Output type: <class 'NoneType'>\n"
     ]
    }
   ],
   "source": [
    "output = hello()\n",
    "\n",
    "print(f\"Output type: {type(output)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here's an example that has two parameters (`number_1`, `number_2`) and returns the sum of those two inputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def sum_numbers(number_1, number_2):\n",
    "    total_sum = number_1 + number_2\n",
    "    return total_sum\n",
    "\n",
    "x = sum_numbers(10,15)\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Documenting Your Function\n",
    "\n",
    "When defining your function, it's very important to include documentation. A function's documentation is called `docstrings`. It typically describes the purpose of your function, what computations it performs, and what gets returned. It also provides information on what your inputs should be. \n",
    "\n",
    "In Python, there are two main styles for writing docstrings. The first style is [Google](http://google.github.io/styleguide/pyguide.html#Comments):\n",
    "\n",
    "```\n",
    "def func(arg1, arg2):\n",
    "    \"\"\"Summary line.\n",
    "\n",
    "    Extended description of function.\n",
    "\n",
    "    Args:\n",
    "        arg1 (int): Description of arg1\n",
    "        arg2 (str): Description of arg2\n",
    "\n",
    "    Returns:\n",
    "        bool: Description of return value\n",
    "\n",
    "    \"\"\"\n",
    "    return True\n",
    "```\n",
    "\n",
    "The second style is [Numpy](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard):\n",
    "\n",
    "```\n",
    "def func(arg1, arg2):\n",
    "    \"\"\"Summary line.\n",
    "\n",
    "    Extended description of function.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    arg1 : int\n",
    "        Description of arg1\n",
    "    arg2 : str\n",
    "        Description of arg2\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    bool\n",
    "        Description of return value\n",
    "\n",
    "    \"\"\"\n",
    "    return True\n",
    "```\n",
    "\n",
    "Before building a Python application, it's a good idea to decide which docstring style you want to use. If your docstrings are short and simple, Google's style is a great option. If you have long, in-depth docstrings, you may want to opt for the Numpy style. That being said, this is mainly a style preference. Both docstring styles are valid.\n",
    "\n",
    "Let's re-write our `sum_numbers()` function with docstrings using the Google style guide."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sum_numbers(number_1, number_2):\n",
    "    \"\"\"Sums two numbers together. \n",
    "    \n",
    "    Args:\n",
    "        number_1 (int): first number to be summed\n",
    "        number_2 (int): second number to be summed\n",
    "    \n",
    "    Returns:\n",
    "        int: sum of number_1 and number_2\n",
    "    \"\"\"\n",
    "    total_sum = number_1 + number_2\n",
    "    return total_sum"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Want to see more examples of Python docstrings in action? Check out the code base of open-source Python packages like [pandas](https://github.com/pandas-dev/pandas/tree/master/pandas) and [scikit-learn](https://github.com/scikit-learn/scikit-learn/tree/master/sklearn) for inspiration. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}