{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Global and local Variables in Functions\n",
    "\n",
    "- http://www.python-course.eu/python3_global_vs_local_variables.php\n",
    "- http://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scope resolution for variable names via the LEGB rule\n",
    "\n",
    "> Local -> Enclosed -> Global -> Built-in\n",
    "\n",
    "- **Local** can be inside a function or class method, for example.\n",
    "- **Enclosed** can be its enclosing function, e.g., if a function is wrapped inside another function.\n",
    "- **Global** refers to the uppermost level of the executing script itself, and\n",
    "- **Built-in** are special names that Python reserves for itself.\n",
    "\n",
    "So, if a particular name:object mapping cannot be found in the local namespaces, the namespaces of the enclosed scope are being searched next. If the search in the enclosed scope is unsuccessful, too, Python moves on to the global namespace, and eventually, it will search the built-in namespace (side note: if a name cannot found in any of the namespaces, a NameError will is raised).\n",
    "\n",
    "![](http://sebastianraschka.com/images/blog/2014/scope_resolution_legb_rule/scope_resolution_1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I love Paris in the summer!\n"
     ]
    }
   ],
   "source": [
    "# global values can be used inside the body of a function\n",
    "def f(): \n",
    "    print(s)\n",
    "\n",
    "\n",
    "s = \"I love Paris in the summer!\"\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I love London!\n",
      "I love Paris!\n"
     ]
    }
   ],
   "source": [
    "# if we change the value of s inside of the function f()? \n",
    "# Will it affect the global variable as well? We test this in the following piece of code:\n",
    "\n",
    "def f(): \n",
    "    s = \"I love London!\"\n",
    "    print(s) \n",
    "\n",
    "\n",
    "s = \"I love Paris!\" \n",
    "f()\n",
    "print(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "local variable 's' referenced before assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mUnboundLocalError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-12-5a4c102b6ea7>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     13\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"I love Paris!\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-12-5a4c102b6ea7>\u001b[0m in \u001b[0;36mf\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# UnboundLocalError: local variable 's' referenced before assignment\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m     \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"I love London!\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 's' referenced before assignment"
     ]
    }
   ],
   "source": [
    "# A variable can't be both local and global inside of a function. \n",
    "# So Python decides that we want a local variable due to the assignment to s inside of f(), \n",
    "# so the first print statement before the definition of s throws the error message above.\n",
    "#\n",
    "# Any variable which is changed or created inside of a function is local, \n",
    "# if it hasn't been declared as a global variable.\n",
    "\n",
    "def f(): \n",
    "    print(s)  # UnboundLocalError: local variable 's' referenced before assignment\n",
    "    s = \"I love London!\"\n",
    "    print(s)\n",
    " \n",
    "s = \"I love Paris!\"\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I am looking for a course in Paris!\n",
      "Only in spring, but London is great as well!\n",
      "Only in spring, but London is great as well!\n"
     ]
    }
   ],
   "source": [
    "# To tell Python, that we want to use the global variable, \n",
    "# we have to explicitly state this by using the keyword \"global\", as can be seen in the following example:\n",
    "\n",
    "def f():\n",
    "    global s\n",
    "    print(s)\n",
    "    s = \"Only in spring, but London is great as well!\"\n",
    "    print(s)\n",
    "\n",
    "\n",
    "s = \"I am looking for a course in Paris!\" \n",
    "f()\n",
    "print(s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Global Variables in Nested Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before calling g: x == 42\n",
      "Calling g now:\n",
      "After calling g: x == 42\n",
      "x in main: 43\n"
     ]
    }
   ],
   "source": [
    "# `global` statement inside of the nested function g does not affect the variable x of the function f.\n",
    "# The global keyword in nested functions does not affect the namespace of their enclosing namespace!\n",
    "\n",
    "def f():\n",
    "    x = 42\n",
    "    def g():\n",
    "        global x\n",
    "        x = 43\n",
    "        \n",
    "    print(\"Before calling g: x == \" + str(x))\n",
    "    print(\"Calling g now:\")\n",
    "    g()\n",
    "    print(\"After calling g: x == \" + str(x))\n",
    "\n",
    "x = 3\n",
    "f()\n",
    "print(\"x in main: \" + str(x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Nonlocal Variables\n",
    "\n",
    "We need a way to access variables of other scopes as well. The way to do this are `nonlocal` definitions, which we will explain in the next chapter.\n",
    "\n",
    "Python3 introduced nonlocal variables as a new kind of variables. `nonlocal` variables have a lot in common with global variables. One difference to global variables lies in the fact that it is not possible to change variables from the module scope, i.e. variables which are not defined inside of a function, by using the nonlocal statement."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    global x\n",
    "    print(x)\n",
    "    \n",
    "x = 3\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "no binding for nonlocal 'x' found (<ipython-input-1-9d390b951067>, line 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-1-9d390b951067>\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m    nonlocal x\u001b[0m\n\u001b[0m    ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m no binding for nonlocal 'x' found\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    nonlocal x\n",
    "    print(x)\n",
    "    \n",
    "x = 3\n",
    "x = 42\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before calling g: 42\n",
      "Calling g now:\n",
      "After calling g: 43\n",
      "x in main: 3\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    x = 42\n",
    "    def g():\n",
    "        nonlocal x\n",
    "        x = 43\n",
    "    print(\"Before calling g: \" + str(x))\n",
    "    print(\"Calling g now:\")\n",
    "    g()\n",
    "    print(\"After calling g: \" + str(x))\n",
    "    \n",
    "x = 3\n",
    "f()\n",
    "print(\"x in main: \" + str(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "no binding for nonlocal 'x' found (<ipython-input-30-89e29c17f55b>, line 4)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-30-89e29c17f55b>\"\u001b[0;36m, line \u001b[0;32m4\u001b[0m\n\u001b[0;31m    nonlocal x\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m no binding for nonlocal 'x' found\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    #x = 42   # We get an error if it isn't defined\n",
    "    def g():\n",
    "        nonlocal x\n",
    "        x = 43\n",
    "    print(\"Before calling g: \" + str(x))\n",
    "    print(\"Calling g now:\")\n",
    "    g()\n",
    "    print(\"After calling g: \" + str(x))\n",
    "    \n",
    "x = 3\n",
    "f()\n",
    "print(\"x in main: \" + str(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before calling g: 3\n",
      "Calling g now:\n",
      "After calling g: 43\n",
      "x in main: 43\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    #x = 42   # We get an error if it isn't defined\n",
    "    def g():\n",
    "        global x # when we change \"nonlocal\" to \"global\",\n",
    "        x = 43\n",
    "    print(\"Before calling g: \" + str(x))\n",
    "    print(\"Calling g now:\")\n",
    "    g()\n",
    "    print(\"After calling g: \" + str(x))\n",
    "    \n",
    "x = 3\n",
    "f()\n",
    "print(\"x in main: \" + str(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "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.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
