{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Modules\n",
    "\n",
    "You can turn any Python script that you write into a module that other people can import and use in their own code.\n",
    "\n",
    "For example;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import superhero"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What has happened here???\n",
    "\n",
    "There is a file in your current directory called `superhero.py`. The line `import superhero` will look in the current directory, to find a file called `superhero.py`. It then runs this file, just as if you had typed it into the screen."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is just a simple Python script, which we can print out using"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for line in open(\"superhero.py\", \"r\").readlines():\n",
    "    print(line, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can get help on the module using `help`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "help(superhero)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This documentation comes from the class and function documentation put into the file.\n",
    "\n",
    "You can also use the data, classes and functions in the file, e.g."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ironman = superhero.Superhero(name=\"Iron Man\", weakness=\"rust\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "superhero.battle(ironman, superhero.lex)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "superhero.lex.steal(\"rust\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "superhero.battle(ironman, superhero.lex)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One thing to note is that all of the classes, functions and data in the script has been imported into its own namespace, named after the script (e.g. `superhero.`). We can import the file and put all names into the current namespace using"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from superhero import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "battle(ironman, lex)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "While any python script can be imported as a module, there are a few conventions you should follow that will make your module easier for others to use.\n",
    "\n",
    "* Add documentation to the module. As you can see, there is a docstring at the top of `superhero.py`, which is the first thing written out by help(). This should provide an overview of the module.\n",
    "* Avoid actually running any code or creating any variables. The current `superhero.py` is bad as it does this, which is why you see \"Is it a bird...\" printed when you import it!\n",
    "\n",
    "The way to avoid creating any variables or running code is to let the script detect when it is being imported, and to not create any variables if that is the case.\n",
    "\n",
    "You can detect if your Python script is *not* being imported using\n",
    "\n",
    "```python\n",
    "if __name__ == \"__main__\":\n",
    "    print(\"I am not being imported.\")\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if __name__ == \"__main__\":\n",
    "    print(\"I am not being imported\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To show how this works, there is a `superhero2.py` script, which is identical to `superhero.py`, except all code that should not be run on import is hidden inside the `if __name__ == \"__main__\":` block."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for line in open(\"superhero2.py\", \"r\").readlines():\n",
    "    print(line, end=\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import superhero2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By using `if __name__ == \"__main__\":` we have prevented `superhero2.py` from printing anything out when it is imported, and have also prevented it from creating the variables `lex` and `superman`.\n",
    "\n",
    "You can see this by running the `superhero2.py` script directory, e.g. using\n",
    "\n",
    "```\n",
    "! python superhero2.py\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! python superhero2.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise\n",
    "\n",
    "## Exercise 1\n",
    "\n",
    "Use the \"New Text File\" option in the Jupyter Home to create a new python text file called `morse.py`. Copy the below class into this file.\n",
    "\n",
    "```python\n",
    "class Morse:\n",
    "    def __init__(self):\n",
    "        self._letter_to_morse = {'a':'.-', 'b':'-...', 'c':'-.-.', 'd':'-..', 'e':'.', 'f':'..-.',\n",
    "                   'g':'--.', 'h':'....', 'i':'..', 'j':'.---', 'k':'-.-', 'l':'.-..', 'm':'--',\n",
    "                   'n':'-.', 'o':'---', 'p':'.--.', 'q':'--.-', 'r':'.-.', 's':'...', 't':'-',\n",
    "                   'u':'..-', 'v':'...-', 'w':'.--', 'x':'-..-', 'y':'-.--', 'z':'--..',\n",
    "                   '0':'-----', '1':'.----', '2':'..---', '3':'...--', '4':'....-',\n",
    "                   '5':'.....', '6':'-....', '7':'--...', '8':'---..', '9':'----.',\n",
    "                   ' ':'/' }\n",
    "        \n",
    "        self._morse_to_letter = {}\n",
    "        for letter in self._letter_to_morse.keys():\n",
    "            self._morse_to_letter[ self._letter_to_morse[letter] ] = letter\n",
    "        \n",
    "    def encode(self, message):\n",
    "        morse = []\n",
    "        for letter in message:\n",
    "            morse.append( self._letter_to_morse[letter.lower()] )\n",
    "        return morse\n",
    "    \n",
    "    def decode(self, morse):\n",
    "        message = []\n",
    "        for code in morse:\n",
    "            message.append( self._morse_to_letter[code] )\n",
    "        return \"\".join(message)\n",
    "```\n",
    "\n",
    "Add documentation to this class, and to the module. Next, import the module and get help using the commands\n",
    "\n",
    "```python\n",
    "import morse\n",
    "help(morse)\n",
    "```\n",
    "\n",
    "Does your documentation make sense?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 2\n",
    "\n",
    "Create some checks of your module that should not be run when the module is imported (i.e. only run directly). The checks should be, e.g.\n",
    "\n",
    "```python\n",
    "    morse = Morse()\n",
    "\n",
    "    for message in [\"Hello world\", \"something to encode\", \"test message\"]:\n",
    "        test = morse.decode( morse.encode(message) )\n",
    "\n",
    "        if message.lower() == test: \n",
    "            print(\"Success: %s\" % message)\n",
    "        else:\n",
    "            print(\"Failed: %s\" % message)\n",
    "```\n",
    "\n",
    "Validate that the check doesn't run on import using \n",
    "\n",
    "```python\n",
    "import morse\n",
    "```\n",
    "\n",
    "Validate that the check runs from the command line using\n",
    "\n",
    "```\n",
    "! python morse.py\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! python morse_answer.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "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.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
