{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Functions\n", "\n", "- [Download the lecture notes](https://philchodrow.github.io/PIC16A/content/functions/functions_1.ipynb). \n", "\n", "\n", "**Functions** are one of the most important constructs in computer programming. A function is a single command which, when executed, performs some operations and may return a value. You've already encountered functions in PIC10A, where they may have looked something like this: \n", "\n", "```cpp\n", "\n", "// Filename: boldy.cpp\n", "\n", "#include \n", "\n", "int main() {\n", " std::cout << \"To boldly go\";\n", " return 0;\n", "}\n", "```\n", "\n", "You'll notice the *type declaration* (`int`), the function name (`main`), the parameter declaration (`()`, i.e. no parameters in this case), and the *return value* (`0`). Python functions have a similar syntax. Instead of a type declaration, one uses the `def` keyword to denote function definition. One does not use `{}` braces, but one does use a `:` colon to initiate the body of the function and whitespace to indent the body. \n", "\n", "Since Python is interpreted rather than compiled, functions are ready to use as soon as they are defined. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To boldly go\n" ] } ], "source": [ "def boldly_print(): # colon ends declaration and begins definition\n", " print(\"To boldly go\") \n", " # return values are optional\n", " \n", "boldly_print()\n", "# ---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parameters\n", "\n", "Just as in C++, in Python we can pass *arguments* (or *parameters*) to functions in order to modify their behavior. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To boldly go\n", "To boldly go\n", "To boldly go\n" ] } ], "source": [ "def boldly_print_2(k): \n", " for i in range(k):\n", " print(\"To boldly go\") \n", "\n", "boldly_print_2(3) \n", "# ---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These arguments can be given *default* values, so that it is not necessary to specify each argument in each function call. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To boldly go\n", "To boldly go\n" ] } ], "source": [ "def boldly_print_3(k, verb=\"go\"):\n", " for i in range(k):\n", " print(\"To boldly \" + verb)\n", " \n", "boldly_print_3(2)\n", "# ---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is often desirable to use *keyword arguments* so that your code clearly indicates which argument is being supplied which value: " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To boldly sing\n", "To boldly sing\n", "To boldly sing\n" ] } ], "source": [ "boldly_print_3(3, \"sing\") # fine\n", "# ---" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To boldly sing\n", "To boldly sing\n", "To boldly sing\n" ] } ], "source": [ "boldly_print_3(k=3, verb=\"sing\") # same as above, easier to read\n", "# ---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All keyword arguments must be supplied after all positional arguments: " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "positional argument follows keyword argument (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m boldly_print_3(k = 3, \"sing\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" ] } ], "source": [ "boldly_print_3(k = 3, \"sing\")\n", "# --- " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scope\n", "\n", "The **global scope** is the set of all variables available for usage outside of any function. " ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 3 # available in global scope\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functions create a **local scope**. This means: \n", "\n", "- Variables in the global scope are available within the function. \n", "- Variables created within the function are **not** available within the global scope. " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] } ], "source": [ "# variables within the global scope are available within the function\n", "def print_x():\n", " print(x)\n", "print_x()\n", "# ---" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "def print_y():\n", " y = 2\n", " print(y)\n", "print_y()\n", "# ---" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'y' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0my\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'y' is not defined" ] } ], "source": [ "y\n", "# ---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Immutable variables in the global scope cannot be modified by functions, even if you use the same variable name. " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7\n" ] } ], "source": [ "def new_x():\n", " x = 7\n", " print(x)\n", "new_x()\n", "# ---" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] } ], "source": [ "print(x)\n", "# ---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On the other hand, *mutable* variables in global scope can be modified by functions. **This is usually a bad idea**, for reasons we'll discuss in another set of notes. " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['kriK', 'draciP', 'yawenaJ', 'oksiS']" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# this works, but it's a bad idea. \n", "captains = [\"Kirk\", \"Picard\", \"Janeway\", \"Sisko\"]\n", "\n", "def reverse_names():\n", " for i in range(4):\n", " captains[i] = captains[i][::-1]\n", "\n", "reverse_names()\n", "captains" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Return values\n", "\n", "So far, we've seen examples of functions that print but do not *return* anything. Usually, you will want your function to have one or more return values. These allow the output of a function to be used in future computations. " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['to boldly dance', 'to boldly dance']" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def boldly_return(k = 1, verb = \"go\"):\n", " return([\"to boldly \" + verb for i in range(k)])\n", "\n", "x = boldly_return(k = 2, verb = \"dance\")\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Your function can return multiple values:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def double_your_number(j):\n", " return(j, 2*j)\n", "\n", "x, y = double_your_number(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `return` statement *immediately* terminates the function's local scope, usually returning to global scope. So, for example, a `return` statement can be used to terminate a `while` loop, similar to a `break` statement. " ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6561" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def largest_power_below(a, upper_bound):\n", " i = 1\n", " while True:\n", " i *= a\n", " if a*i >= upper_bound:\n", " return(i)\n", " \n", "largest_power_below(3, 10000)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.8.3" } }, "nbformat": 4, "nbformat_minor": 4 }