{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# User-defined functions\n", "- named sequence of statements that execute together to solve some task \n", "- primary purpose is to help us break the problem into smaller sub-problems or tasks\n", "- two types: fruitful and void/fruitless functions\n", "- must be defined before it can be used or called (two step process)\n", "- syntax to define a function:\n", "```python\n", "def functionName( PARAMETER1, PARAMETER2, ... ):\n", " # STATEMENTS\n", " return VALUE\n", "```\n", "- PARAMETERS and return statements are OPTIONAL\n", "- function NAME follows the same rules as a variable/identifier name\n", "- some built-in functions and object methods have been used...\n", "- syntax to call function\n", "\n", "```python\n", "VARIABLE = functionName( ARGUMENT1, ARGUMENT2, ...)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## why functions?\n", "**dividing a program into functions or sub-programs have several advantages:**\n", "- give you an opportunity to name a group of statements, which makes your program easier to read and debug\n", "- can make a program smaller by eliminating repetitive code. Later, if you make a change, you only have to make it in one place\n", "- allow you to debug the parts one at a time (in a team) and then assemble them into a working whole\n", "- write once, test, share, and reuse many times (libraries, e.g.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## two-step process\n", "1. define function\n", "- call/use function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## void/fruitless functions" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Function definition\n", "def greet():\n", " print('Hello World!')" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello World!\n" ] } ], "source": [ "# Function call\n", "greet()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello World!\n", "a = None\n" ] } ], "source": [ "# void function; returns None by default\n", "a = greet() # returned value\n", "print('a =', a)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(greet)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# function can be assigned to a variable\n", "myfunc = greet\n", "type(myfunc)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello World!\n" ] } ], "source": [ "myfunc()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## passing data/arguments to functions\n", "- functions are subprograms that may need external data to work with\n", "- one can pass data to functions via parameters/arguments\n", "- can provide default values to parameters" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "scrolled": true }, "outputs": [], "source": [ "def greet(name):\n", " print('Hello {0}'.format(name))" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello John Smith\n" ] } ], "source": [ "greet('John Smith')" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "scrolled": true }, "outputs": [ { "ename": "TypeError", "evalue": "greet() missing 1 required positional argument: 'name'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\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[0mgreet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# How to fix? provide either default value or call it properly\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: greet() missing 1 required positional argument: 'name'" ] } ], "source": [ "greet() # How to fix? provide either default value or call it properly" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "scrolled": true }, "outputs": [], "source": [ "def greet(name=\"Anonymous\"):\n", " print('Hello {0}'.format(name))" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello Anonymous\n" ] } ], "source": [ "greet()" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Enter your name: \n", "Hello \n" ] } ], "source": [ "user = input('Enter your name: ')\n", "greet(user)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### arguments are local to functions" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "scrolled": true }, "outputs": [ { "ename": "NameError", "evalue": "name 'name' 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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'name' is not defined" ] } ], "source": [ "print(name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### visualize variables' scope using pythontutor\n", "https://goo.gl/TyXPg6" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "var1 = Bob\n", "var2 = John\n", "a = 1\n", "b = Apple\n", "c = 4.5\n", "*args = (5, [2.5, 'b'])\n", "type of args = \n", "**kwargs = {'fname': 'Jake', 'num': 1}\n", "type of kwargs = \n", "Bob\n" ] } ], "source": [ "# global and local scope demos with various ways to pass arguments\n", "var1 = \"Alice\" #global\n", "def myFunc(a, b, c, *args, **kwargs):\n", " global var1\n", " var1 = \"Bob\" # global or local? How can we access global var1?\n", " var2 = \"John\"\n", " print('var1 = ', var1)\n", " print('var2 = ', var2)\n", " print('a = ', a)\n", " print('b = ', b)\n", " print('c = ', c)\n", " print('*args = ', args)\n", " print('type of args = ', type(args))\n", " print('**kwargs = ', kwargs)\n", " print('type of kwargs = ', type(kwargs))\n", "\n", "myFunc(1, 'Apple', 4.5, 5, [2.5, 'b'], fname='Jake', num=1)\n", "print(var1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## fruitful functions\n", "- functions that return some value(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Problem: \n", "- Define a function that takes two numbers as arguments\n", "- function then returns the sum of those two numbers" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "def add(num1, num2):\n", " \"\"\"\n", " takes two numeric arguments: num1 and num2\n", " calculates and returns the sume of num1 and num2\n", " \"\"\"\n", " total = num1 + num2\n", " return total" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "300" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# call add to run and test\n", "add(100, 200)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "90.99" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add(100.99, -10)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function add in module __main__:\n", "\n", "add(num1, num2)\n", " takes take numeric arguments, num1 and num2\n", " returns the sum of num1 and num2\n", "\n" ] } ], "source": [ "help(add)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# complete the following function\n", "def multiply(x, y):\n", " \"\"\"\n", " Function takes two numbers, x and y.\n", " Returns the product of x and y.\n", " FIXME\n", " \"\"\"\n", " pass" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function multiply in module __main__:\n", "\n", "multiply(x, y)\n", " Function takes two numbers, x and y.\n", " Returns the product of x and y.\n", " FIXME\n", "\n" ] } ], "source": [ "# help can be run for user-defined functions as well\n", "help(multiply)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## assert statement for automated testing\n", " - assert statement can be used to automatically test fruitful functions\n", " - each assertion must be True or pass to continue to the next\n", " - if assertion fails, throws AssertionError exception and program halts" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "# uint testing add function\n", "assert add(2, 3) == 5\n", "assert add(10, -5) == 5\n", "# assert add(100, 2000.99) == ?" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "# unit testing multiply function\n", "# write some sample test cases for multiply function using assert statement" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### functions can return more than 1 values" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "scrolled": true }, "outputs": [], "source": [ "def getAreaAndPerimeter(length, width):\n", " \"\"\"\n", " Function takes length and width of a rectangle.\n", " Finds and returns area and perimeter of the rectangle.\n", " \"\"\"\n", " area = length*width\n", " perimeter = 2*(length+width)\n", " return area, perimeter" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true }, "source": [ "### variable length arguments\n", "- *args (non-keyworded variable length arguments)\n", "- *kwargs (keyworded variable length arguments)\n", "- use when not sure how many arguments will be passed to the function" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true, "scrolled": true }, "source": [ "## pass by value\n", "- passing immutable variables (string, int, float) as arguments to functions by copying them" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello Jake\n", "var1 = John\n" ] } ], "source": [ "var1 = 'John' # global variable\n", "def greetSomeone(arg1):\n", " arg1 = 'Jake' # local variable\n", " print('hello ', arg1)\n", " \n", "greetSomeone(var1)\n", "print('var1 = ', var1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## visualize pass by value\n", "https://goo.gl/1MbS6y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercises" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### exercise 1\n", "Write a function that takes two numbers; subtracts the second from the first and returns the difference." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### exercise 2\n", "
\n",
    "Write a function that converts seconds to hours, minutes and seconds. Function then returns the values in HH:MM:SS format (e.g., 01:09:10)\n",
    "\n",
    "Here are some tests that should pass:\n",
    "assert get_time(3666) == '01:01:06'\n",
    "assert get_time(36610) == '10:10:10'\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### exercise 3\n", "
\n",
    "Write a function called hypotenuse that returns the length of the hypotenuse of a right triangle given the lengths of the two legs as parameters:\n",
    "\n",
    "assert hypotenuse(3, 4) == 5.0\n",
    "assert hypotenuse(12, 5) == 13.0\n",
    "assert hypotenuse(24, 7) == 25.0\n",
    "assert hypotenuse(9, 12) == 15.0\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### exercise 4\n", "
\n",
    "Write a function slope(x1, y1, x2, y2) that returns the slope of the line through the points (x1, y1) and (x2, y2). Be sure your implementation of slope can pass the following tests:\n",
    "\n",
    "assert slope(5, 3, 4, 2) == 1.0\n",
    "assert slope(1, 2, 3, 2) == 0.0\n",
    "assert slope(1, 2, 3, 3) == 0.5\n",
    "assert slope(2, 4, 1, 2) == 2.0\n",
    "\n",
    "Then use a call to slope in a new function named intercept(x1, y1, x2, y2) that returns the y-intercept of the line through the points (x1, y1) and (x2, y2)\n",
    "\n",
    "assert intercept(1, 6, 3, 12) == 3.0\n",
    "assert intercept(6, 1, 1, 6) == 7.0\n",
    "assert intercept(4, 6, 12, 8) == 5.0\n",
    "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.7.2" } }, "nbformat": 4, "nbformat_minor": 2 }