{ "cells": [ { "cell_type": "markdown", "id": "0ba08993", "metadata": {}, "source": [ "--- \n", " \n", "\n", "

Department of Data Science

\n", "

Course: Tools and Techniques for Data Science

\n", "\n", "---\n", "

Instructor: Muhammad Arif Butt, Ph.D.

" ] }, { "cell_type": "markdown", "id": "007946c5", "metadata": {}, "source": [ "

Lecture 2.11

" ] }, { "cell_type": "markdown", "id": "21764bfc", "metadata": {}, "source": [ "\"Open" ] }, { "cell_type": "markdown", "id": "ae18f83a", "metadata": {}, "source": [ "## _Python-Functions.ipynb_" ] }, { "cell_type": "markdown", "id": "4377d061", "metadata": {}, "source": [ "## Learning agenda of this notebook\n", "\n", "1. What are functions in Python \n", "2. User Defined Functions in Python\n", " - Basic Examples\n", " - Docstring inside a function\n", " - Passing arguments and Returning value from a function\n", " - Pass by value vs Pass by reference\n", "3. Arguments of a Python function \n", " - Positional/Required arguments\n", " - Default arguments\n", " - Keyword arguments\n", " - Variable length arguments\n", " - Arbitrary key word arguments\n", "4. Passing Command Line Arguments in Python\n", "5. Nested functions\n", "6. Understanding Scope in Python\n", " - Local scope\n", " - Enclosing scope\n", " - Global scope\n", " - Built-in Scope" ] }, { "cell_type": "markdown", "id": "f7fefd5f", "metadata": {}, "source": [ "## 1. What are Functions in Python\n", "- A function in a programming language is group of related statements with a name that perform a specific task.\n", "- The code inside a function executes only when it is called, and it can be called from anywhere in the program any number of times.\n", "- So, functions help break our program into smaller and modular chunks, avoids repetition, makes the code reusable, organized and manageable.\n", "- You can pass data/parameter to a function, which carry out some operation on the data and may or may not return a result.\n", "- *FUNCTIONS*: Functions are created by function definitions and are called using `func(arguments)` notation\n", " - *Built-in Functions*: are part of the standard Python library, e.g., `print()`, `len()`\n", " - *User Defined Functions*: are defined by the programmer him/herself (can be anonymous)\n", "- *METHODS*: Methods are functions that are called using the attribute notation. `object.method(arguments)`\n", " - *Built-in Methods*\n", " - *Class Instance Methods*" ] }, { "cell_type": "code", "execution_count": null, "id": "1169a2f1", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "ed95e9ae", "metadata": {}, "outputs": [], "source": [ "help('FUNCTIONS')" ] }, { "cell_type": "code", "execution_count": null, "id": "bc07c709", "metadata": {}, "outputs": [], "source": [ "help('METHODS')" ] }, { "cell_type": "markdown", "id": "0ca4bb39", "metadata": {}, "source": [ "## 2. How to Create User Defined Functions in Python\n", " " ] }, { "cell_type": "code", "execution_count": null, "id": "d92f6a4f", "metadata": {}, "outputs": [], "source": [ "help('def')" ] }, { "cell_type": "markdown", "id": "efe504e4", "metadata": {}, "source": [ "### a. Basic Example of a Python Function" ] }, { "cell_type": "code", "execution_count": null, "id": "4d10c27f", "metadata": {}, "outputs": [], "source": [ "def mylen(a):\n", " count = 0\n", " for a in a:\n", " count = count +1\n", " return count\n", "\n", "\n", "str = [1 ,2 ,6, 8]\n", "mylen(str)" ] }, { "cell_type": "code", "execution_count": null, "id": "fd42b55e", "metadata": {}, "outputs": [], "source": [ "v = \"Hello World, this is fun\"\n", "rv = mylen(v)\n", "print(rv)" ] }, { "cell_type": "code", "execution_count": null, "id": "7ce34e7c", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "1383b896", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "7d73a9f3", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "befafbe1", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "f8035790", "metadata": {}, "outputs": [], "source": [ "help(len)" ] }, { "cell_type": "code", "execution_count": null, "id": "ad038cb3", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "f3202bf2", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "f8ffad16", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "f295e6bd", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "d52ab7ed", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "fa813286", "metadata": {}, "outputs": [], "source": [ "def func(a, b):\n", " c = a + b\n", " return c\n", " \n", "rv = func(2, 3)\n", "print(rv)" ] }, { "cell_type": "code", "execution_count": null, "id": "29cc57ef", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "7256fc43", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "ae8c5e02", "metadata": {}, "outputs": [], "source": [ "def func(a):\n", " count = 2\n", " count = count + a\n", " return count\n", "\n", "b = int(input(\"Enter a number:\"))\n", "c = func(b)\n", "print(c)" ] }, { "cell_type": "code", "execution_count": null, "id": "3af60d3d", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "c6157164", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "cafa16e3", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "c4751b3d", "metadata": {}, "outputs": [], "source": [ "#defining a function\n", "def func1():\n", " print(\"Functions in Python\")\n", "\n", "#calling a function (A function must be defined before it is called)\n", "func1()" ] }, { "cell_type": "code", "execution_count": null, "id": "8e1296a3", "metadata": {}, "outputs": [], "source": [ "func1()" ] }, { "cell_type": "markdown", "id": "6b55cae4", "metadata": {}, "source": [ "### b. Docstring inside a Function\n", "- We can add some documentation within our function using a *docstring*. \n", "- A docstring is simply a string that appears as the first statement within the function body, and is used by the `help` function. \n", "- A good docstring describes what the function does, and provides some explanation about the arguments." ] }, { "cell_type": "code", "execution_count": null, "id": "c7865185", "metadata": {}, "outputs": [], "source": [ "#defining a function\n", "def func1():\n", " \"\"\" This is a docstring that describes what the function do\n", " It simply display a welcome message\"\"\"\n", " print(\"Welcome to Learning Functions in Python\")\n", "\n", "#calling a function\n", "func1()" ] }, { "cell_type": "code", "execution_count": null, "id": "5d7e47da", "metadata": {}, "outputs": [], "source": [ "# We can access the docstring using the built-in command __doc__. \n", "# Any identifier that starts with a double underscore is a Python builtin command\n", "func1.__doc__" ] }, { "cell_type": "code", "execution_count": null, "id": "699c7c89", "metadata": {}, "outputs": [], "source": [ "help(func1)" ] }, { "cell_type": "markdown", "id": "47ba9a3d", "metadata": {}, "source": [ "### c. Returning value from a Function\n", "* The Python `return` statement is used to end the execution of the function or method and send the function’s result back to the caller. A return statement consists of the `return` keyword followed by an optional return value.\n", "* The statements (if any) after the `return` statement are not executed.\n", "* A Python function or method can return numeric values (int, float, and complex values), collections and sequences of objects (list, tuple, dictionary, or set objects), user-defined objects, classes, functions, and even modules or packages.\n", "* You can omit the return value of a function and use a bare `return` keyword without a return value. You can also omit the entire return statement. In both cases, the return value will be None.\n", "* You can use a return statement to return multiple values from a function. To do that, you just need to supply several return values separated by commas." ] }, { "cell_type": "code", "execution_count": null, "id": "978b8814", "metadata": {}, "outputs": [], "source": [ "help('return')" ] }, { "cell_type": "code", "execution_count": null, "id": "a370f371", "metadata": {}, "outputs": [], "source": [ "#defining a function\n", "def mysum2(): \n", " total = 5 + 7\n", " return total\n", "\n", "#calling a function\n", "rv = mysum2()\n", "print(\"5 + 7 = \", rv)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "74a7838e", "metadata": {}, "outputs": [], "source": [ "#Returning multiple values from a function\n", "def func(): \n", " str1 = \"hello\"\n", " str2= \"bye\"\n", " return str1, str2\n", "\n", "#calling a function\n", "rv1, rv2 = func()\n", "print(rv1)\n", "print(rv2)\n", "\n", "rv = func()\n", "print(rv)" ] }, { "cell_type": "markdown", "id": "ab9d1b5e", "metadata": {}, "source": [ "### d. Arguments to a Python Function\n", "- Functions can accept zero or more values as *inputs* (also knows as *arguments* or *parameters*). Arguments help us write flexible functions that can perform the same operations on different values. Further, functions can return a result that can be stored in a variable or used in other expressions.\n", "- Arguments are called **required arguments** means that if your function expects 2 arguments, you have to call the function with 2 arguments, not more, and not less.\n", "- Arguments are called **positional arguments** because while calling the function, the arguments must be passed in the correct positional order to get the desired result. e.g., in case of `subtract(a, b)`\n", "- The number of required arguments passed to a Python function is limited by available process stack frame" ] }, { "cell_type": "code", "execution_count": null, "id": "8e8201b9", "metadata": {}, "outputs": [], "source": [ "# A function that is passed two numbers and it returns their sum\n", "def mysum3(a, b): \n", " \"\"\"Calculates and return the sum of two numbers.\n", " Arguments:\n", " a - First number \n", " b - Second number\n", " \"\"\"\n", " total = a + b\n", " return total\n", "\n", "#calling a function\n", "a = 10 \n", "b = 15\n", "rv = mysum3(a, b)\n", "print(a, \" + \", b, \" = \", rv)" ] }, { "cell_type": "code", "execution_count": null, "id": "eec70cae", "metadata": {}, "outputs": [], "source": [ "help(mysum3)" ] }, { "cell_type": "code", "execution_count": null, "id": "b39cbe0a", "metadata": {}, "outputs": [], "source": [ "# A function that receives a list and returns a Number data type containing sum of squares of its elements \n", "def sumofsquares(l1):\n", " rv = 0\n", " for i in l1:\n", " rv = rv + i*i\n", " return rv\n", "\n", "#calling a function\n", "list1 = [1, 2, 3] \n", "rv = sumofsquares(list1)\n", "\n", "print(\"Sum of Square of List elements are: \", rv)\n", "print(\"Return type = \", type(rv))" ] }, { "cell_type": "code", "execution_count": null, "id": "6e1d5603", "metadata": {}, "outputs": [], "source": [ "# A function that receives a list and returns a List data type containing sub-list containing even numbers\n", "def filter_even(number_list):\n", " result_list = []\n", " for number in number_list:\n", " if (number % 2 == 0):\n", " result_list.append(number)\n", " return result_list\n", "\n", "even_list = filter_even([1, 2, 3, 4, 5, 6, 7])\n", "even_list\n", "print(type(even_list))" ] }, { "cell_type": "markdown", "id": "a774b7e0", "metadata": {}, "source": [ "### e. Pass by value vs Pass by reference\n", "- All arguments (less intrinsic types) in Python are passed by reference. It means if you change what a parameter refers to within a function, the change also reflects back in the calling function." ] }, { "cell_type": "code", "execution_count": null, "id": "db5d17ae", "metadata": {}, "outputs": [], "source": [ "# Function arguments of intrinsic types like int, float, strings are passed by value\n", "def myfunc(nam):\n", " nam = \"Arif Butt\"\n", "\n", "a = \"Kakamanna\"\n", "print(\"Before calling: \", a)\n", "\n", "rv = myfunc(a)\n", "print(\"After calling: \", a)" ] }, { "cell_type": "code", "execution_count": null, "id": "d11cdf0f", "metadata": {}, "outputs": [], "source": [ "# Function arguments of intrinsic types like int, float, strings are passed by value\n", "def myfunc(x, y, z):\n", " x = x + 1\n", " y = y + 1\n", " z = z + 1\n", "\n", "a = 10\n", "b = 20\n", "c = 30\n", "\n", "myfunc(a, b, c)\n", "\n", "print(a, b, c)" ] }, { "cell_type": "code", "execution_count": null, "id": "3eaec931", "metadata": {}, "outputs": [], "source": [ "# Lists, Tuples, Sets, and Dictionary objects are passed to functions as reference\n", "# The changes made by the callee are visible to the caller (but not for tuples as they are immutable)\n", "def func1(l1):\n", " l1[2] = 'x'\n", " \n", "mylist = ['a', 'b', 'c', 'd', 'e', 'f']\n", "print(\"Before calling: \", mylist)\n", "\n", "func1(mylist)\n", "print(\"After calling: \", mylist)" ] }, { "cell_type": "code", "execution_count": null, "id": "05bd102a", "metadata": {}, "outputs": [], "source": [ "# Example: \n", "def func1(list2):\n", " l3 = list2[1:4]\n", " l3[0] = 'x'\n", " print(\"List l3 is local to the function having: \", l3)\n", " \n", "list1 = ['a', 'b', 'c', 'd', 'e', 'f']\n", "print(\"Before calling: \", list1)\n", "\n", "func1(list1)\n", "print(\"After calling: \", list1)" ] }, { "cell_type": "code", "execution_count": null, "id": "bffa44b9", "metadata": {}, "outputs": [], "source": [ "# Example: \n", "def func1(list2):\n", " l3 = list2[1:4]\n", " print(\"List l3 is local to the function having: \", l3)\n", " return l3\n", " \n", "list1 = ['a', 'b', 'c', 'd', 'e', 'f']\n", "print(\"Before calling list1: \", list1)\n", " \n", "returned_list = func1(list1)\n", "print(\"After calling list1: \", list1)\n", "print(\"Returned list is : \", returned_list)" ] }, { "cell_type": "code", "execution_count": null, "id": "85b1e826", "metadata": {}, "outputs": [], "source": [ "# Tuples are mutable: proof of concept\n", "def func1(t1):\n", " t1[2] = 'x'\n", " \n", "mytuple = ('a', 'b', 'c', 'd', 'e', 'f')\n", "print(\"Before calling: \", mytuple)\n", "\n", "func1(mytuple)\n", "print(\"After calling: \", mytuple)" ] }, { "cell_type": "markdown", "id": "bff3c86a", "metadata": {}, "source": [ "**A List sorting example to differentiate between Pass By Value and Pass by Reference**" ] }, { "cell_type": "code", "execution_count": null, "id": "d32f7b98", "metadata": {}, "outputs": [], "source": [ "# Example: The function sort the list that is passed by reference (Selection Sort)\n", "def sel_sort1(mylist): \n", " for i in range(len(mylist)):\n", " min_idx = i\n", " for j in range(i+1, len(mylist)):\n", " if mylist[min_idx] > mylist[j]:\n", " min_idx = j \n", " mylist[i], mylist[min_idx] = mylist[min_idx], mylist[i] # Swap minimum element with the first element\n", "\n", "numbers = [25, 15, -6, 8, 2]\n", "rv = sel_sort1(numbers)\n", "print(\"Passed list is sorted: \", numbers)\n", "print(\"Returned value: \", rv)" ] }, { "cell_type": "code", "execution_count": null, "id": "0dd8c748", "metadata": {}, "outputs": [], "source": [ "# Example: The function creates a new copy of the list that is passed by reference, sort the copy and return\n", "# The passed list remains unchanged\n", "def sel_sort2(mylist):\n", " newlist = mylist[:]\n", " for i in range(len(mylist)):\n", " min_idx = i\n", " for j in range(i+1, len(mylist)):\n", " if newlist[min_idx] > newlist[j]:\n", " min_idx = j \n", " newlist[i], newlist[min_idx] = newlist[min_idx], newlist[i] # Swap minimum element with the first element\n", " return newlist\n", "\n", "numbers = [25, 15, -6, 8, 2]\n", "rv = sel_sort2(numbers)\n", "print(\"Returned list: \", rv)\n", "print(\"Passed list is unchanged: \", numbers)" ] }, { "cell_type": "markdown", "id": "929039be", "metadata": {}, "source": [ "## 3. Function Arguments in Python\n", "- There are following points that one needs to keep in mind while using arguments in Python functions:\n", " - Required Arguments / Positional arguments\n", " - Default Arguments\n", " - Named/Keyword Arguments\n", " - Variable length Arguments\n", " - Arbitrary Keyword Arguments" ] }, { "cell_type": "markdown", "id": "853c5dfb", "metadata": {}, "source": [ "### a. Required/Positional arguments\n", "- If a function expect two arguments, you have to call the function with exactly two arguments.\n", "- Moreover, arguments must be passed in correct positional order to get the desired result." ] }, { "cell_type": "code", "execution_count": null, "id": "9389e0ab", "metadata": {}, "outputs": [], "source": [ "def mysub(a, b):\n", " return a - b\n", "\n", "x = 8\n", "y = 3\n", "# calling a function with both arguments (order matters)\n", "rv = mysub(x, y)\n", "rv" ] }, { "cell_type": "code", "execution_count": null, "id": "28b3f3c7", "metadata": {}, "outputs": [], "source": [ "mysub(3, 2, 4)" ] }, { "cell_type": "markdown", "id": "6360f7ac", "metadata": {}, "source": [ "### b. Default arguments\n", "- In a function definition, we can assign default values to arguments.\n", "- During function call, if a value is not passed to that argument, the function assumes the default value." ] }, { "cell_type": "code", "execution_count": null, "id": "0f05f612", "metadata": {}, "outputs": [], "source": [ "# Function with default arguments\n", "def display(name = 'kakamanna', age = 35):\n", " print (\"Name: \", name, \", Age: \", age)\n", " return;\n", "\n", "# calling a function with both arguments (order matters)\n", "display(\"Arif Butt\", 51)\n", "\n", "# calling a function with one argument only (the default value of age will be printed)\n", "display(\"Mujahid Butt\" )\n" ] }, { "cell_type": "code", "execution_count": null, "id": "68e1c341", "metadata": {}, "outputs": [], "source": [ "# You cannot skip the first default argument and give the second\n", "display(,51 )\n", "#Solution is keyword arguments (discussed below)" ] }, { "cell_type": "markdown", "id": "22e9c9dd", "metadata": {}, "source": [ "### c. Keyword/Named arguments\n", "- If you want to bypass the positional argument rule, we can pass arguments in any order by mentioning their parameter names, which the function definition is expecting.\n", "- Using **keyword/named arguments**, a programmer can pass arguments in any order by mentioning their parameter names while calling the function" ] }, { "cell_type": "code", "execution_count": null, "id": "93177a85", "metadata": {}, "outputs": [], "source": [ "# Function calling with key word arguments\n", "def display(name, age):\n", " print (\"Name: \", name, \", Age: \", age)\n", " return;\n", "\n", "# Sequence/order of arguments matter\n", "display(25, \"Arif Butt\")" ] }, { "cell_type": "code", "execution_count": null, "id": "71cf6ca4", "metadata": {}, "outputs": [], "source": [ "# Sequence/order of arguments DOES NOT matter now\n", "display(age=25, name=\"Mujahid Butt\") # passing parameters in any order using keyword argument " ] }, { "cell_type": "code", "execution_count": null, "id": "1e8a53f8", "metadata": {}, "outputs": [], "source": [ "def mysub(a, b):\n", " return a - b\n", "\n", "\n", "# calling a function using named arguments is always a better programming practice\n", "rv = mysub(b = 3, a = 8)\n", "rv" ] }, { "cell_type": "markdown", "id": "2d7c5bf3", "metadata": {}, "source": [ "### d. Variable length arguments\n", "- Although we can pass a list to a function containing any number of elements.\n", "- But sometimes, we need more flexibility while defining functions like we don't know in advance the fixed number of arguments.\n", "- Python allows us to make function calls with variable length arguments.\n", "- If you want a function to receive variable number of arguments, you place an asterisk (`*`) before the variable name.\n", "- This way the function will receive a tuple of arguments (an iterable), and can access the items accordingly" ] }, { "cell_type": "code", "execution_count": null, "id": "f131f51f", "metadata": {}, "outputs": [], "source": [ "def my_function(*args): # Whatever is passed to this function, it will create an iterable out of it\n", " print(type(args))\n", "\n", "my_function('arif','rauf')\n", "print(\"\\n\")" ] }, { "cell_type": "code", "execution_count": null, "id": "e98931c6", "metadata": {}, "outputs": [], "source": [ "# Example: Passing variable number of arguments to a function\n", "def my_function(*args): # Whatever is passed to this function, it will create an iterable out of it\n", " for i in args: # We can use the iter() and next() function to iterate through the iterable\n", " print(i, end=' ')\n", "\n", "my_function()\n", "my_function('arif','rauf')\n", "print(\"\\n\")\n", "\n", "my_function(1, 2, 3, 4, 5, 6, 7, 8)\n", "print(\"\\n\")\n", "\n", "my_function(5, 2.5, 9)" ] }, { "cell_type": "markdown", "id": "f23e215b", "metadata": {}, "source": [ "### e. Arbitrary keyword arguments\n", "- Arbitrary keyword arguments (`**kwarg`) is just like variable length arguments (`*arg`). The difference is instead of accepting positional arguments, it accepts keyword (or named) arguments.\n", "- When using the ** parameter, the order of arguments does not matter. However, the name of the arguments must be the same.\n", "- This way the function will receive a dictionary of arguments, and you can access the items accordingly" ] }, { "cell_type": "code", "execution_count": null, "id": "0205f0d6", "metadata": {}, "outputs": [], "source": [ "def myfunc(**kwargs):\n", " # Iterating over the key:value pairs of kwargs dictionary\n", " for arg in kwargs.items():\n", " print(arg)\n", "\n", " \n", "myfunc(a = \"Learning\", b = 'Is', c = 'Fun')" ] }, { "cell_type": "code", "execution_count": null, "id": "3515bdbb", "metadata": {}, "outputs": [], "source": [ "def myfunc(**kwargs):\n", " result = \"\"\n", " # Iterating over the values only of kwargs dictionary\n", " for arg in kwargs.values():\n", " print(arg)\n", "\n", "myfunc(a = \"Learning\", b = 'Is', c = 'Fun', d ='with', e='Arif')" ] }, { "cell_type": "code", "execution_count": null, "id": "9acfc7e1", "metadata": {}, "outputs": [], "source": [ "def greet(**kwargs):\n", " print('Hello, ', kwargs['fname'], kwargs['mname'], kwargs['lname'])\n", "\n", "greet(lname='Butt', fname='Muhammad', mname= 'Arif')\n" ] }, { "cell_type": "code", "execution_count": null, "id": "f1c241ee", "metadata": {}, "outputs": [], "source": [ "def myconcat(**kwargs):\n", " result = \"\"\n", " # Iterating over the values of kwargs dictionary\n", " for arg in kwargs.values():\n", " result += arg + ' '\n", " return result\n", "\n", "rv = myconcat(a = \"Learning\", b='Is', c='Fun')\n", "rv" ] }, { "cell_type": "markdown", "id": "0e2dccaa", "metadata": {}, "source": [ "## 4. Passing Command Line Arguments in Python\n", "- The arguments that are given after the name of the program in the command line shell of the operating system are known as Command Line Arguments. \n", "- Python provides various ways of dealing with these types of arguments. The three most common are: \n", " - Using `sys.argv`\n", " - Using `getopt` module\n", " - Using `argparse` module" ] }, { "cell_type": "code", "execution_count": null, "id": "410bd5fc", "metadata": {}, "outputs": [], "source": [ "%pycat cmd_arg1.py" ] }, { "cell_type": "code", "execution_count": null, "id": "c7c740ec", "metadata": { "scrolled": false }, "outputs": [], "source": [ "# %load cmd_arg1.py\n", "# Python script to demonstrate command line arguments\n", "\n", "import sys\n", "\n", "# `sys.argv` is a list of command line arguments \n", "n = len(sys.argv) # number of command line arguments.\n", "print(\"Total arguments passed:\", n)\n", " \n", "print(\"\\nName of Python script:\", sys.argv[0]) #name of the current Python script. \n", " \n", "print(\"\\nArguments passed:\", end = \" \")\n", "for i in range(1, n):\n", " print(sys.argv[i], end = \" \")\n", " \n", "# add command line arguments and print result\n", "sum = 0\n", "for i in range(1, n):\n", " sum += int(sys.argv[i])\n", " \n", "print(\"\\n\\nResult:\", sum)" ] }, { "cell_type": "code", "execution_count": null, "id": "b8b9fe8c", "metadata": {}, "outputs": [], "source": [ "%run cmd_arg1.py 5 7 2 -10" ] }, { "cell_type": "markdown", "id": "31f31e66", "metadata": {}, "source": [ "## 5. Functions can be Nested in Python\n", "- A function that is defined inside another function is called nested or inner function.\n", "- Nested or inner function can access variables created in the outer function (enclosing scope).\n", "- Inner functions have many uses, most notably as closure factories and decorator functions." ] }, { "cell_type": "code", "execution_count": null, "id": "fed321a2", "metadata": {}, "outputs": [], "source": [ "def outerFunction(): \n", " name = 'Arif'\n", " def innerFunction():\n", " print(name) \n", " innerFunction() \n", " \n", "outerFunction() \n", "#innerFunction() # This line will raise a NameErrror\n", " # because an innerFunction() can only be accessed in the outerFunction() body, and not outside it" ] }, { "cell_type": "markdown", "id": "0f49370f", "metadata": {}, "source": [ "## 6. Understanding Scope in Python\n", "- **Scope of Variable** means the part of program where we can access that particular variable. \n", "- **Lifetime of a variable** is the period throughout which the variable exists in memory. The lifetime of a variable inside a function is as long as the function executes. They are destroyed once we return from the function. Hence, a function does not remember the value of a variable from its previous calls.\n", "- **Symbol Table**: Python interpreter maintains a data structure called symbol table (using a dictionary object) containing information about each identifier appearing in the program's source code. \n", "\n", " \n", "\n", "- In Python, there are 4 types of Variable Scopes\n", " >- Local Scope\n", " >- Nonlocal/Enclosing Scope\n", " >- Global Scope\n", " >- Built-in Scope\n" ] }, { "cell_type": "markdown", "id": "d2ec5ee0", "metadata": {}, "source": [ "### a. Understanding Local Scope\n", ">**Local Scope:** Python first tries to search for an identifier (variable) in Local scope. The local variable exists only within the block/function that it is declared in. When that block/function ends, the local variable has no significance, it gets destroyed. We cannot use it outside the function where it is declared." ] }, { "cell_type": "code", "execution_count": null, "id": "4086958d", "metadata": {}, "outputs": [], "source": [ "# Example 1: Understanding Local Scope\n", "# The variable 'bb' declared inside the function is local to that function\n", "# When you try to access (read/write) it outside the function, Python raises a NameError\n", "\n", "def my_function():\n", " bb = 1234 # a new local variable named 'a' is created\n", " print(\"Value of variable 'bb' inside function: \", bb)\n", "\n", "my_function()\n", "#print(\"Value of variable 'bb' outside function: \", bb) #Raise NameError, as the variable 'bb' no longer exists" ] }, { "cell_type": "markdown", "id": "4c344bbd", "metadata": {}, "source": [ "### b. Understanding Enclosing Scope\n", ">**Enclosing Scope:** Enclosing (or nonlocal) scope is a special scope that only exists for nested functions. If Python does not find an identifier (variable) within the local scope, it will examine the Enclosing scope to see if it can find the variable there." ] }, { "cell_type": "code", "execution_count": null, "id": "335d0e82", "metadata": {}, "outputs": [], "source": [ "# Example 1: Understanding Enclosing / Non-Local Scope\n", "def f1():\n", " x = 4\n", " def f2():\n", " print(x) #Since there is no variable 'x' defined in f2(), so it will search it in the non-local scope\n", " f2()\n", " print(x) #The variable 'x' is defined in the local scope of f1() function\n", "\n", "f1()" ] }, { "cell_type": "code", "execution_count": null, "id": "de61c550", "metadata": {}, "outputs": [], "source": [ "# Example 2: Understanding Enclosing / Non-Local Scope\n", "def f1():\n", " cc = 1234 # cc is local variable to f1()\n", " def f2():\n", " cc = 4321 # cc is local variable to f2()\n", " print(\"Inside the f2() function: cc = \", cc)\n", " f2()\n", " print(\"Inside the f1() function: cc = \", cc)\n", "\n", "\n", "f1()" ] }, { "cell_type": "markdown", "id": "bdc5fee5", "metadata": {}, "source": [ "### c. Understanding Global Scope\n", ">- **Global Scope:** A global variable is accessible from anywhere in your script, including from within a function. It is usually defined at the top of the script or outside of the function. \n", ">- Python first tries to find an identifier in the local scope, then in the non-local/enclosing scope. If it cannot find it in those two scopes then it will search the identifier in the global scope." ] }, { "cell_type": "code", "execution_count": null, "id": "5dd92692", "metadata": {}, "outputs": [], "source": [ "# Example 1: Understanding Global Scope\n", "\n", "b = 9999 # a global variable\n", "def my_function():\n", " print(\"Value of variable 'b' inside function: \", b)\n", "\n", "my_function()\n", "print(\"Value of global variable 'b' outside function: \", b)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "6f53cdd9", "metadata": {}, "outputs": [], "source": [ "# Example 2: Understanding Global Scope\n", "c = 1234 # a global variable named 'c'\n", "def my_function():\n", " c = 4321 # a new local variable named 'c' is created\n", " print(\"Value of variable 'c' inside function: \", c)\n", "\n", "my_function()\n", "# Over here the local variable c containing 4321 does not exist, therefore, Python interpreter will look \n", "# in the non-local/enclosing scope, it also doesnot contain variable c, so\n", "# finally the Python interpreter will look in the global scope, where it exist with value of 1234\n", "print(\"Value of variable 'c' outside function: \", c)\n" ] }, { "cell_type": "markdown", "id": "457d6ed9", "metadata": {}, "source": [ "### d. Understanding Built-in Scope\n", ">- The Built-in scope has all the functions and variables that are there, when we start the Python interpreter, e.g., the `print()`, `len()` and `id()` functions are in the built-in scope.\n", ">- If an identifier is not found in local, enclosing and global scopes within a module, then Python will examine the built-in scope to see if it is defined there. " ] }, { "cell_type": "code", "execution_count": null, "id": "d3f36cae", "metadata": {}, "outputs": [], "source": [ "# Example: Since the identifier `len` is not found in local, enclosing and global scopes, therefore,\n", "# Python would consult the Built-In scope, where it will find the len function and outputs 12\n", "x = len ('Data Science') \n", "print(x) " ] }, { "cell_type": "code", "execution_count": null, "id": "0f15f10b", "metadata": {}, "outputs": [], "source": [ "# Example: Since the identifier `len` is there in the local scope, therefore\n", "# Python would use the `len` function defined in local scope and not the Built-In scope, and outputs 54\n", "def len(x):\n", " return 54\n", "\n", "x = len('Data Science') \n", "print(x)" ] }, { "cell_type": "markdown", "id": "70bf759b", "metadata": {}, "source": [ "### e. Use of `global` Keyword\n", ">- The `global` keyword is used to tell the Python interpreter to use the globally defined variable instead of locally defining it. \n", ">- Let us understand this with example:" ] }, { "cell_type": "markdown", "id": "362d6cf1", "metadata": {}, "source": [ "**We cannot update the value of a global variable inside a function (local scope). If you try to do so it will raise an error**" ] }, { "cell_type": "code", "execution_count": null, "id": "ae006ff2", "metadata": {}, "outputs": [], "source": [ "# Example:\n", "c = 1234 # c is a global variable\n", "def my_function():\n", " c = c + 1 # Updating global variable inside function\n", " print(\"Value of variable 'c' inside function: \", c)\n", "\n", "my_function()\n", "print(\"Value of variable 'c' outside function: \", c)\n" ] }, { "cell_type": "markdown", "id": "ef7d7524", "metadata": {}, "source": [ "**If you want to update the value of a global variable inside a function, simply type `global` followed by the variable name. This will tell Python interpreter to use the globally defined variable instead of locally defining it**" ] }, { "cell_type": "code", "execution_count": null, "id": "264f2875", "metadata": {}, "outputs": [], "source": [ "# Example: To update a global variable inside a function, you use the global keyword\n", "d = 1234\n", "def my_function():\n", " global d # global keyword does not create a new local variable, rather allows you to access the global var\n", " d = d + 1 # Updating global variable inside function\n", " print(\"Value of variable 'd' inside function: \", d)\n", "\n", "my_function()\n", "print(\"Value of variable 'd' outside function: \", d)" ] }, { "cell_type": "markdown", "id": "7ad5f54b", "metadata": {}, "source": [ "### f. Use of `nonlocal` Keyword\n", ">- Python `nonlocal` keyword is used to make the variable which refers to the variable bounded in the nearest scope.\n", ">- Scope to which variable it bound should not be global or local scope.\n", ">- The main use of nonlocal variable is in a nested function." ] }, { "cell_type": "code", "execution_count": null, "id": "08f9e88c", "metadata": {}, "outputs": [], "source": [ "# Example 1: You get an error if you try to update a non-local variable inside a function\n", "def f1():\n", " a = 1234\n", " def f2():\n", " a = a + 1\n", " print(\"Inside the f2() function: a = \", a)\n", " f2()\n", " print(\"Inside the f1() function: a = \", a)\n", "\n", "\n", "f1()" ] }, { "cell_type": "code", "execution_count": null, "id": "70485c83", "metadata": {}, "outputs": [], "source": [ "# Example 2: To update a nonlocal variable inside the inner function, you use the nonlocal keyword\n", "def f1():\n", " a = 1234\n", " def f2():\n", " nonlocal a\n", " a = a + 1\n", " print(\"Inside the f2() function: a = \", a)\n", " f2()\n", " print(\"Inside the f1() function: a = \", a)\n", "\n", "\n", "f1()" ] }, { "cell_type": "code", "execution_count": null, "id": "17a100fd", "metadata": {}, "outputs": [], "source": [ "# QUIZ: Give the output by assuming memory addresses at your own.\n", "# Do mention the scope of all the identifiers used\n", "x = 5\n", "print(x, id(x))\n", "def number():\n", " x = 3\n", " print(x, id(x))\n", " def f1():\n", " nonlocal x\n", " x = x * 5\n", " print(x, id(x))\n", " f1()\n", "def numb():\n", " global x\n", " x = x * 5\n", " print(x, id(x))\n", "number()\n", "numb() \n", "print(x, id(x))" ] }, { "cell_type": "code", "execution_count": null, "id": "70027c25", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "d9de52f9", "metadata": {}, "source": [ "## Check your Concepts\n", "\n", "Try answering the following questions to test your understanding of the topics covered in this notebook:\n", "\n", "1. What is a function?\n", "2. What are the benefits of using functions?\n", "3. What are some built-in functions in Python?\n", "4. How do you define a function in Python? Give an example.\n", "5. What is the body of a function?\n", "6. When are the statements in the body of a function executed?\n", "7. What is meant by calling or invoking a function? Give an example.\n", "8. What are function arguments? How are they useful?\n", "9. How do you store the result of a function in a variable?\n", "10. What is the purpose of the `return` keyword in Python?\n", "11. Can you return multiple values from a function?\n", "12. Can a `return` statement be used inside an `if` block or a `for` loop?\n", "13. Can the `return` keyword be used outside a function?\n", "14. What is scope in a programming region? \n", "15. How do you define a variable inside a function?\n", "16. What are local & global variables?\n", "17. Can you access the variables defined inside a function outside its body? Why or why not?\n", "18. What do you mean by the statement \"a function defines a scope within Python\"?\n", "19. Do for and while loops define a scope, like functions?\n", "20. Do if-else blocks define a scope, like functions?\n", "21. What are optional function arguments & default values? Give an example.\n", "22. Why should the required arguments appear before the optional arguments in a function definition?\n", "23. How do you invoke a function with named arguments? Illustrate with an example.\n", "24. Can you split a function invocation into multiple lines?\n", "25. Write a function that takes a number and rounds it up to the nearest integer.\n", "26. What is a docstring? Why is it useful?\n", "27. How do you display the docstring for a function?\n", "28. What are *args and **kwargs? How are they useful? Give an example.\n", "29. Can you define functions inside functions? \n", "30. What is function closure in Python? How is it useful? Give an example.\n", "31. What is recursion? Illustrate with an example.\n", "32. Can functions accept other functions as arguments? Illustrate with an example.\n", "33. Can functions return other functions as results? Illustrate with an example.\n", "34. What are decorators? How are they useful?\n", "35. Implement a function decorator which prints the arguments and result of wrapped functions.\n", "36. What are some in-built decorators in Python?\n", "37. Can you invoke a function inside the body of another function? Give an example.\n", "38. What is the single responsibility principle, and how does it apply while writing functions?\n", "39. What some characteristics of well-written functions?\n", "40. Can you use if statements or while loops within a function? Illustrate with an example.\n", "41. Compare the use of lambda functions in sorted(), map(), filter(), reduce(), and accumulate() functions and their different use cases.\n", "42. Check out the use of command line arguments in Python using `sys.argv[]`, and `getopt.getopt()`\n", "43. The pdb module implements an interactive debugging environment for Python programs. It includes features to let you pause your program, look at the values of variables, and watch program execution step-by-step, so you can understand what your program actually does and find bugs in the logic. Python Debugger (pdb): https://docs.python.org/3.8/library/pdb.html\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ea4d5f09", "metadata": {}, "outputs": [], "source": [ "a = 5 \n", "print(a)\n", "def myfunc():\n", " a = 3\n", " print(a)\n", " def f1():\n", " nonlocal a\n", " a = a * 2\n", " print(a)\n", " f1()\n", " print(a)\n", "\n", "myfunc() \n", "print(a)" ] }, { "cell_type": "code", "execution_count": null, "id": "63703608", "metadata": {}, "outputs": [], "source": [] } ], "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.7" } }, "nbformat": 4, "nbformat_minor": 5 }