ASAP 3 HRS!! PYTHON ASSIGNMENT
{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Week 01 \n", "---\n", "\n", "\n", "\n", "---\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Contents\n", "---\n", "\n", "In this section, we will review the programming language Python and also provide some more detailed examples of the ideas from the previous section. If you are new to Python or find that you need more information about any of the topics presented, we recommend that you consult a resource such as the Python Language Reference or a Python Tutorial. Our goal here is to reacquaint you with the language and also reinforce some of the concepts that will be central to later chapters." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1 Set-Up\n", "---\n", "\n", "The following cell contains some set-up commands. Be sure to execute this cell before continuing." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Notebook Set-Up Commands\n", "\n", "import math\n", "\n", "def print_error(error):\n", " \"\"\" Print Error\n", " \n", " Function to print exceptions in red.\n", " \n", " Parameters\n", " ----------\n", " error : string\n", " Error message\n", " \n", " \"\"\"\n", " print('\\033[1;31m{}\\033[1;m'.format(error))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2 A Quick Recap\n", "---\n", "\n", "Before getting into the specifics of Pythonic coding, let's take a second to refresh the basics. In order to understand the topics covered in this notebook you will need to know the following:\n", "\n", "### Basic Python object types " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Object is of type: <class 'int'>\n", "Object is of type: <class 'float'>\n", "Object is of type: <class 'bool'>\n", "Object is of type: <class 'str'>\n", "Object is of type: <class 'list'>\n", "Object is of type: <class 'tuple'>\n", "Object is of type: <class 'set'>\n" ] } ], "source": [ "# Basic Python objects\n", "\n", "myint = 1\n", "print('Object is of type:', type(myint))\n", "\n", "myfloat = 1.0\n", "print('Object is of type:', type(myfloat))\n", "\n", "mybool = True\n", "print('Object is of type:', type(mybool))\n", "\n", "mystring = 'hello'\n", "print('Object is of type:', type(mystring))\n", "\n", "mylist = [1, 2, 3]\n", "print('Object is of type:', type(mylist))\n", "\n", "mytuple = (1, 2, 3)\n", "print('Object is of type:', type(mytuple))\n", "\n", "myset = set([1, 1, 2])\n", "print('Object is of type:', type(myset))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Standard operators" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Addition: 1 + 2 = 3\n", "Subtraction: 1 - 2 = -1\n", "Multiplication: 4 * 2 = 8\n", "Division: 4 / 2 = 2.0\n", "Floor Division: 4 // 2 = 2\n", "Exponentiation: 4 ** 2 = 16\n" ] } ], "source": [ "# Standard operators\n", "\n", "print('Addition: 1 + 2 = ', 1 + 2)\n", "print('Subtraction: 1 - 2 = ', 1 - 2)\n", "print('Multiplication: 4 * 2 = ', 4 * 2)\n", "print('Division: 4 / 2 = ', 4 / 2)\n", "print('Floor Division: 4 // 2 = ', 4 // 2)\n", "print('Exponentiation: 4 ** 2 = ', 4 ** 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Simple logic operations" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True and True = True\n", "True and False = False\n", "True or True = True\n", "True or False = True\n" ] } ], "source": [ "# Simple logic operations\n", "\n", "print('True and True = ', True and True)\n", "print('True and False = ', True and False)\n", "print('True or True = ', True or True)\n", "print('True or False = ', True or False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Functions" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello world!\n", "My value is 5.\n" ] } ], "source": [ "# Functions\n", "\n", "def say_hello():\n", " print('Hello world!')\n", " \n", "def show_value(value):\n", " print('My value is {}.'.format(value))\n", " \n", "say_hello()\n", "show_value(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3 Lambda Functions\n", "---\n", "\n", "To get started, let's take second to remember how functions work in Python. Functions are defined with the keyword `def` followed by the function name, the function arguments in `()` and a colon `:` to end the definition. On the following lines the function behaviour is coded and if the function returns an object this is provided to the keyword `return`.\n", "\n", "Let's look at an example of a function that calculates the square of an input value." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Function to calculate the square of the input value\n", "def square_func(x):\n", " \n", " return x ** 2\n", "\n", "square_func(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python provides a useful shorthand for writing simple functions of this type using the keyword `lambda`.\n", "\n", "Let's look at an example to perform the same squaring function." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Lambda function to calculate the square of the input value\n", "square_lambda = lambda x: x ** 2\n", "\n", "square_lambda(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The syntax is `lambda` followed by the function arguments, a colon to end the definition, and finally the function behaviour is automatically returned. Like all Python functions, lambda functions are objects (we will come back to this idea) and since they are not named, they need to be assigned to an object in order to be used.\n", "\n", "Here is another example using multiple arguments." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Lambda function to calculate the sum of two input values\n", "add = lambda x, y: x + y\n", "\n", "add(1, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While lambda functions are extremely useful, best practices dictate that they should be used sparingly. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4 Unpacking\n", "---\n", "\n", "A fundamental first step on your journey of Pythonic coding is understanding how handle array-like objects (*i.e.* lists and tuples).\n", "\n", "### Basic Unpacking\n", "\n", "As you are no doubt aware lists and tuples can be indexed by passing an element number inside `[]`..." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mytuple[1] = 2\n" ] } ], "source": [ "# Create a tuple\n", "mytuple = (1, 2, 3)\n", "\n", "# Get index 1 from the tuple\n", "print('mytuple[1] =', mytuple[1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... however, it is also possible to *unpack* these objects." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "val1 = 1 ; val2 = 2 ; val3 = 3\n" ] } ], "source": [ "# Unpack a tuple\n", "val1, val2, val3 = (1, 2, 3)\n", "\n", "print('val1 = {} ; val2 = {} ; val3 = {}'.format(val1, val2, val3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that when three objects are assigned to the tuple each value is unpacked automatically, but if we attempt to unpack three values into two objects we raise an exception." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;31mtoo many values to unpack (expected 2)\u001b[1;m\n" ] } ], "source": [ "# Try to unpack values into two objects\n", "try:\n", " val1, val2 = (1, 2, 3)\n", "except Exception as error:\n", " print_error(error)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Positional Expansion\n", "\n", "We can bypass the problem using the *splat* (`*`) operator. In addition to be used as the standard operator for multiplication, `*` also has special unpacking features." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mytuple = (1, 2, 3)\n", "unpacked mytuple = 1 2 3\n" ] } ], "source": [ "# Print mytuple\n", "print('mytuple =', mytuple)\n", "\n", "# Print mytuple after unpacking\n", "print('unpacked mytuple =', *mytuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this operator we can now unpack our three-element tuple into just two objects." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "val1 = 1 ; val2 = [2, 3]\n" ] } ], "source": [ "# Unpack a tuple\n", "val1, *val2 = mytuple\n", "\n", "print('val1 = {} ; val2 = {}'.format(val1, val2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that the first value was unpacked and the remaining values were packed into a list.\n", "\n", "> **Puzzle 1:** Guess the value of `val2` in the following example." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# Create a list\n", "mylist = [7, 2, 8, 2, 6]\n", "\n", "# Unpack the list\n", "val1, *val2, val3 = mylist\n", "\n", "# Uncomment to see the answer\n", "# print(val2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also recursively unpack array-like objects.\n", "\n", "> **Puzzle 2:** Guess the value of `b` in the following cell." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# Create a list of tuples\n", "list_of_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]\n", "\n", "# Unpack the list\n", "*A, B = list_of_tuples\n", "\n", "# Unpack one of the tuples\n", "*a, b = A[1]\n", "\n", "# Uncomment to see the answer\n", "# print('b =', b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Further Reading\n", "> - <a href=\"https://medium.com/understand-the-python/understanding-the-asterisk-of-python-8b9daaa4a558\" target=\"_blank\">Understanding the Asterisk of Python</a>" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Unpacking with Functions\n", "\n", "Unpacking can be particularly useful for passing arguments to functions. *e.g.* if you want to pass a single object to a function that expects multiple arguments." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The result of myfunc is 6.\n" ] } ], "source": [ "# Define a function that takes multiple arguments\n", "def myfunc(a, b, c):\n", " \n", " return a + b - c\n", "\n", "# Set a tuple of values\n", "myvalues = (4, 5, 3)\n", "\n", "# Unpack the values into the function\n", "print('The result of myfunc is {}.'.format(myfunc(*myvalues)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, you could pass multiple values to a function that does not know how many arguments to expect." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "count_agrs received 3 arguments.\n" ] } ], "source": [ "# Define a function that has an unknown number of arguments\n", "def count_agrs(*args):\n", " \n", " return len(args)\n", "\n", "# Pass multiple values to the function\n", "print('count_agrs received {} arguments.'.format(count_agrs(1, 2, 3)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5 Dictionaries\n", "---\n", "\n", "Python provides many different ways of storing multiple values in a single object (*e.g.* lists, tuples, sets, *etc*). One of the most useful, and certainly one of the most important, are *<a href=\"https://docs.python.org/2/tutorial/datastructures.html#dictionaries\" target=\"_blank\">dictionaries</a>*. \n", "\n", "Dictionaries are designated with `{}` and are comprised of two main components: *keys* and *values*. The benefit that dictionaries provide with respect to simpler objects like lists is the ability to label values. This makes it a lot easier to store a large number of values in a single object without losing track of what they are.\n", "\n", "### First Example\n", "\n", "Let's look at a concrete example. Imagine we want to keep track of the colours associated to the Teenage Mutant Ninja Turtles.\n", "\n", "<img src=\"http://cdn.shopify.com/s/files/1/0342/0081/products/tmnt_1987_grande.jpg?v=1416367192\" width=\"800\">\n", "\n", "We could start by defining a unique object for each turtle." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Leonardo wears blue.\n" ] } ], "source": [ "# Unique objects for turtle colours\n", "Leonardo = 'blue'\n", "Raphael = 'red'\n", "Donatello = 'purple'\n", "Michelangelo = 'orange'\n", "\n", "print('Leonardo wears {}.'.format(Leonardo))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It might be nicer instead to define a single object." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Raphael wears red.\n" ] } ], "source": [ "# List of turtle colours\n", "turtles = ['blue', 'red', 'purple', 'orange']\n", "\n", "print('Raphael wears {}.'.format(turtles[1]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This, however, assume you will remember the order and names of the turtles. A dictionary makes it possible to retain both the simplicty of a list, but the details of the single objects.\n", "\n", "To define a dictionary object we need to specify a series of keys followed by a colon (`:`) and then the corresponding values, all comma separated and within `{}`." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "# Dictionary of turtle names and colours\n", "turtles = {'Leo': 'blue', 'Raph': 'red', 'Donny': 'purple', 'Mickey': 'orange'}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can look at the pieces that make up the dictionary by looking at the `keys`, `values` and `items`." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict: {'Leo': 'blue', 'Raph': 'red', 'Donny': 'purple', 'Mickey': 'orange'}\n", "keys: dict_keys(['Leo', 'Raph', 'Donny', 'Mickey'])\n", "values: dict_values(['blue', 'red', 'purple', 'orange'])\n", "items: dict_items([('Leo', 'blue'), ('Raph', 'red'), ('Donny', 'purple'), ('Mickey', 'orange')])\n" ] } ], "source": [ "# Show dictionary components\n", "print('dict:', turtles)\n", "print('keys:', turtles.keys())\n", "print('values:', turtles.values())\n", "print('items:', turtles.items())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use the keys (*i.e.* the turtles' names) to look up the values (*i.e.* the corresponding colours). *e.g.* To look up what colour Donatello wears we simply need to pass the key `'Donny'` to the dictionary `turtles` as follows.\n", "\n", "```python\n", "turtles['Donny']\n", "```" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Donatello wears purple.\n" ] } ], "source": [ "# Look up dictionary value|\n", "print('Donatello wears {}.'.format(turtles['Donny']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Note that dictionary look up in Python is highly optimised, but it only works one way (*i.e.* keys → values).\n", "\n", "It is possible to add entries to a dictionary by specifying a new key and assigning a corresponding value." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Leo': 'blue', 'Raph': 'red', 'Donny': 'purple', 'Mickey': 'orange', 'Splinter': 'wine'}\n" ] } ], "source": [ "# Add dictionary entry\n", "turtles['Splinter'] = 'wine'\n", "print(turtles)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to modify existing entry values by takinging an existing key and assigning a new value." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Leo': 'blue', 'Raph': 'red', 'Donny': 'purple', 'Mickey': 'orange', 'Splinter': 'yellow'}\n" ] } ], "source": [ "# Modify dictionary value\n", "turtles['Splinter'] = 'yellow'\n", "print(turtles)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You will raise an exception if you try to index a dictionary or look up a key for a entry that doesn't exist." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;31mKeyError: 'Rocksteady'\u001b[1;m\n" ] } ], "source": [ "try:\n", " turtles['Rocksteady']\n", "except Exception as error:\n", " print_error('KeyError: {}'.format(error))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dictionary Unpacking\n", "\n", "Similarly to array-like objects, dictionaries can be unpacked." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "val1 = a ; val2 = b ; val3 = c\n" ] } ], "source": [ "# Create a dictionary\n", "mydict = {'a' : 1, 'b' : 2, 'c' : 3}\n", "\n", "# Unpack the dictionary\n", "val1, val2, val3 = mydict\n", "\n", "print('val1 = {} ; val2 = {} ; val3 = {}'.format(val1, val2, val3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that by default unpacking is performed over the keys. To unpack the values you have to be more explicit." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "val1 = 1 ; val2 = 2 ; val3 = 3\n" ] } ], "source": [ "# Unpack the dictionary values\n", "val1, val2, val3 = mydict.values()\n", "\n", "print('val1 = {} ; val2 = {} ; val3 = {}'.format(val1, val2, val3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Puzzle 3:** What values will you get when you unpack the dictionary items?" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "# Unpack the dictionary values\n", "val1, val2, val3 = mydict.items()\n", "\n", "# Uncomment to see the answer\n", "# print('val1 = {} ; val2 = {} ; val3 = {}'.format(val1, val2, val3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Keyword Arguments\n", "\n", "In addition to standard position arguments, Python functions accept *keyword arguments* (or simply *kwagrs*), *i.e.* a dictionary of keys and values. \n", "\n", "kwargs can be used to define default values in functions." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "myfunc = 7\n" ] } ], "source": [ "# Define a function with defaults\n", "def myfunc(a=1, b=2, c=2):\n", " \n", " return a + b * 3\n", "\n", "print('myfunc =', myfunc())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "They can also be used to pass arguments in any given order." ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "myfunc = 24\n" ] } ], "source": [ "print('myfunc =', myfunc(c=5, a=3, b=7))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is also a special keyword expansion operator `**` thay behaves similarly to the splat operator `*` for use in functions." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "myfunc = 15\n" ] } ], "source": [ "# Create dictionary of values\n", "mydict = {'a': 3, 'b': 4, 'c': 5}\n", "\n", "# Unpack the dictionary kwargs into the function\n", "print('myfunc =', myfunc(**mydict))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, you can define functions for which an idefinite number of kwargs can be passed." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 - 2 = -1\n", "|1 - 2| = 1\n", "1 - 2 + 3 = 2\n" ] } ], "source": [ "# Define a function with optional kwargs\n", "def subtract(a, b, **kwargs):\n", " \n", " res = a - b\n", " \n", " if 'return_abs' in kwargs and kwargs['return_abs']:\n", " res = abs(res)\n", " \n", " if 'add_value' in kwargs:\n", " res += kwargs['add_value']\n", "\n", " return res\n", " \n", "print('1 - 2 = {}'.format(subtract(1, 2)))\n", "print('|1 - 2| = {}'.format(subtract(1, 2, return_abs=True)))\n", "print('1 - 2 + 3 = {}'.format(subtract(1, 2, add_value=3, dummy_arg=None)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tips and Tricks\n", "\n", "There are plenty of useful tricks that make dictionary handling a lot easier.\n", "\n", "For example, you can convert a list of tuples to a dictionay." ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "List: [('alien', '1979'), ('the shining', '1980'), ('the evil dead', '1981'), ('blade runner', '1982')]\n", "\n", "Dict: {'alien': '1979', 'the shining': '1980', 'the evil dead': '1981', 'blade runner': '1982'}\n", "\n", "Alien came out in 1979.\n" ] } ], "source": [ "# Tuples of film names and release dates\n", "film1 = ('alien', '1979')\n", "film2 = ('the shining', '1980')\n", "film3 = ('the evil dead', '1981')\n", "film4 = ('blade runner', '1982')\n", "\n", "# List of films\n", "films = [film1, film2, film3, film4]\n", "print('List: ', films)\n", "print()\n", "\n", "# Dictionary of films\n", "films = dict(films)\n", "print('Dict: ', films)\n", "print()\n", "print('Alien came out in {}.'.format(films['alien']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "<img src=\"http://t3.gstatic.com/images?q=tbn:ANd9GcSKWeplicF676cMRKV8kqkCErnbNxp6Sm2XQyrrjGNpoLp_lrjI\" width=\"400\">\n", "\n", "You can concatinate two dictionaries in a single line." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Concatination: {'alien': '1979', 'the shining': '1980', 'the evil dead': '1981', 'blade runner': '1982', 'WarGames': '1983', 'Amadeus': '1984'}\n" ] } ], "source": [ "# Make another dictionary\n", "more_films = dict([('WarGames', '1983'), ('Amadeus', '1984')])\n", "\n", "# Concatinate the dictionaries\n", "films_80s = {**films, **more_films}\n", "print('Concatination: ', films_80s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can clear the contents of an existing dictionary using the `clear` method." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Clear: {}\n" ] } ], "source": [ "# Clear the films dictionary\n", "films.clear()\n", "print('Clear: ', films)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, you can add, look up and remove dicttionary entries using the `update`, `get` and `pop` methods respectively." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Update: {'Back to the Future': '1985'}\n", "\n", "Get: Back to the Future came out in 1985.\n", "\n", "Pop: {}\n" ] } ], "source": [ "# Add a value to the dictionary\n", "films.update({'Back to the Future': '1985'})\n", "print('Update:', films)\n", "print()\n", "\n", "# Get the value for the key\n", "print('Get: Back to the Future came out in {}.'.format(films.get('Back to the Future')))\n", "print()\n", "\n", "# Pop the entry out of the dictionary\n", "films.pop('Back to the Future')\n", "print('Pop: ', films)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Note that the `get` method will not raise an error if the key is not found, instead it will simply return `None`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Further Reading \n", "> - <a href=\"https://realpython.com/python-dicts/\" target=\"_blank\">Python dicts</a>" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6 List Comprehension\n", "---\n", "\n", "This section introduces the concept of *list comprehension*, a tool that you will likely have seen implemented but perhaps not understood. The most sensible way to begin understanding this concept is by looking at basic loops.\n", "\n", "### Loops\n", "\n", "Loops are a core component of any programming language and Python is certainly no different." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "i = 1\n", "i = 2\n", "i = 3\n" ] } ], "source": [ "# Simple loop\n", "for i in (1, 2, 3):\n", " print('i =', i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we can see the use of a basic `for` statement using the variable `i`, which iterates through the values in a tuple.\n", "\n", "That being said, Python does offer an extremely powerful way to generate lists from loops using a process called list comprehension. In lower level lanaguages (*e.g.* C, Fortran, *etc.*) arrays are usually built by declaring an empty object of a given size and type and then iteratively filling this array. It is possible to do something similar in Python, in fact, this is often done by people who come to Python from one of these languages." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "my_int_list = [1, 2, 3]\n" ] } ], "source": [ "# Create a list of size 3\n", "my_int_list = [None, None, None]\n", "\n", "# Run a loop\n", "for i in (1, 2, 3):\n", " # Fill the list values\n", " my_int_list[i - 1] = i\n", " \n", "print('my_int_list =', my_int_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While this works, it goes against most of what Python aims to achieve. Using Python list properties we can improve a little bit." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "my_int_list = [1, 2, 3]\n" ] } ], "source": [ "# Create an empty list\n", "my_int_list = []\n", "\n", "# Run a loop\n", "for i in range(1, 4):\n", " # Add list values\n", " my_int_list.append(i)\n", " \n", "print('my_int_list =', my_int_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Note that `range` is a built-in function that produces a list-like object of values within a given range.\n", "\n", "This approach may be appropriate in certain situations, particularly if we plan to *break* the loop when a condition is met. For this particular example, however, there is a much easier way to generate the desired object.\n", "\n", "### List Comprehension\n", "\n", "To perform list comprehension we need the following structure: `[final_expression for_loop_and_conditions]`. For example, to generate the same list object as the previous cell we can do this:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "my_int_list = [1, 2, 3]\n" ] } ], "source": [ "# Create an object through list comprehension\n", "my_int_list = [i for i in range(1, 4)]\n", "\n", "print('my_int_list =', my_int_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example the first `i` is the final expression we want (*i.e.* simply the value), this is followed by a for loop that is identical to that in the loop example.\n", "\n", "Using list comprehension it is possible to create a list object in one line! This may seem trivial, but it is actually a very useful and highly optimised tool in Python.\n", "\n", "### Absolute Magnitude Example\n", "\n", "Imagine you need to calculate the absolute magnitude \n", "\n", "$$M = m - 5\\log_{10}(d_{\\textrm{pc}}) + 5$$\n", "\n", "of various stars:\n", "\n", "|Star|$m_v$|$d_{\\textrm{pc}}$|\n", "|----|-----|-----------------|\n", "|Alpha Centauri|-0.3|1.3|\n", "|Canopus|-0.72|30.1|\n", "|Rigel|0.14|276.1|\n", "|Deneb|1.26|490.8|\n", "\n", "<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Orion_constellation_map.svg/2560px-Orion_constellation_map.svg.png\" width=\"400\">\n", "\n", "One way would be be to store the information about the stars in a dictionary (as we just learned about them)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The apparent magnitude of Rigel is 0.14.\n" ] } ], "source": [ "# Dictionary of star properties\n", "stars = dict([('alpha centauri', {'mv': -0.3, 'dpc': 1.3}), \n", " ('canopus', {'mv': -0.72, 'dpc': 30.1}), \n", " ('rigel', {'mv': 0.14, 'dpc': 276.1}), \n", " ('deneb', {'mv': 1.26, 'dpc': 490.8})])\n", "\n", "print('The apparent magnitude of Rigel is {}.'.format(stars['rigel']['mv']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and to loop through the stars." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Absolute magnitude values = [4.13, -3.11, -7.07, -7.19]\n" ] } ], "source": [ "# Create an empty list\n", "abs_mags = []\n", "\n", "# Loop through the stars\n", "for star in stars.values():\n", " # Calculate absolute magnitude\n", " mag = round(star['mv'] - 5 * math.log10(star['dpc']) + 5, 2)\n", " # Add value to list\n", " abs_mags.append(mag)\n", " \n", "print('Absolute magnitude values =', abs_mags)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A more Pythonic approach, however, would be to define a reusable function and generate the final object via list comprehension." ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Absolute magnitude values = [4.13, -3.11, -7.07, -7.19]\n" ] } ], "source": [ "# Define a function to calculate absolute magnitude\n", "def abs_mag(mv, dpc):\n", " \n", " return round(mv - 5 * math.log10(dpc) + 5, 2)\n", "\n", "# Produce a list of absolute magnitude values\n", "abs_mags = [abs_mag(star['mv'], star['dpc']) for star in stars.values()]\n", "\n", "print('Absolute magnitude values =', abs_mags)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This way, we not only have a function that can be used for future calculations, but we also generate our object in one line.\n", "\n", "### Redshift Example\n", "\n", "Let's look at another example. Imagine you want to calculate the redshift of an object given the emitted and observed wavelenths.\n", "\n", "$$z = \\frac{\\lambda_{\\textrm{obs}}-\\lambda_{\\textrm{emit}}}{\\lambda_{\\textrm{emit}}}$$\n", "\n", "|$\\lambda_{\\textrm{emit}}$|$\\lambda_{\\textrm{obs}}$|\n", "|-------------------------|------------------------|\n", "|450|679|\n", "|348|956|\n", "|579|264|\n", "\n", "We can approach this in a very similar way to the previous example, except in this case let's try without using a dictionary." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Redshifts = [0.509, 1.747, -0.544]\n" ] } ], "source": [ "# Create lists of wavelength values\n", "lambda_emit = [450., 348., 579.]\n", "lambda_obs = [679., 956., 264.]\n", "\n", "# Define a function to calculate redshift\n", "def redshift(emit, obs):\n", " \n", " return round((obs - emit) / emit, 3)\n", "\n", "# Generate redshift values via list comprehension\n", "z = [redshift(e, o) for e, o in zip(lambda_emit, lambda_obs)]\n", "\n", "print('Redshifts =', z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Puzzle 4:** Note the use of the `zip` function. Any guess for what it does?" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "# Uncomment to see the answer\n", "# print('The result of zip is:', list(zip(lambda_emit, lambda_obs)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conditions\n", "\n", "It is possible to embed a complex series of conditions (*e.g.* `if` statements) into list comprehension." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Names containing the letter \"e\" and starting with a letter lower than \"k\": ['jessica', 'gunrey']\n" ] } ], "source": [ "# A list of names\n", "names = ['paul', 'jessica', 'leto', 'gunrey', 'duncan', 'piter']\n", "\n", "# A new list of names satisfying various conditions\n", "new_names = [name for name in names if 'e' in name and name[0] < 'k']\n", "\n", "print('Names containing the letter \"e\" and starting with a letter lower than \"k\":', new_names)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Nested Lists\n", "\n", "It is also possible to flatten nested lists using unpacking and conditions.\n", "\n", "<img src=\"https://www.sccpre.cat/mypng/detail/231-2312516_thundercats-logo-logo-thundercats.png\" width=\"200\">" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Thundercats: ['lion-o', 'cheetara', 'tygra', 'panthro', 'wilykit', 'wilycat', 'snarf']\n" ] } ], "source": [ "# Nested list\n", "cats = [['lion-o', 'cheetara', 'tygra', 'panthro'], ['wilykit', 'wilycat'], ['snarf'], ['mumm-ra']]\n", "\n", "# Flatten list with condition\n", "cats = [cat for group in cats for cat in group if cat!='mumm-ra']\n", "\n", "print('Thundercats:', cats)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Further Reading \n", "> - <a href=\"https://medium.com/better-programming/list-comprehension-in-python-8895a785550b\" target=\"_blank\">List Comprehension in Python</a>" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 7 Iterators & Generators\n", "---\n", "\n", "Some very useful tools in Python, particularly when it comes to memory management, are iterators and generators.\n", "\n", "### Iterators\n", "\n", "The `iter` function can be used to convert objects to iterators, such as lists..." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "myiter = <list_iterator object at 0x7fd054cde2e0>\n" ] } ], "source": [ "# Create list iterator\n", "myiter = iter([1, 2, 3, 4])\n", "\n", "print('myiter =', myiter)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or tuples." ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "myiter = <tuple_iterator object at 0x7fd054cdebb0>\n" ] } ], "source": [ "# Create tuple iterator\n", "myiter = iter((1, 2, 3, 4))\n", "\n", "print('myiter =', myiter)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Puzzle 5:** How would you go about converting an iterator into a list?" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "# Uncomment and edit the following lines appropriately\n", "# Convert iterator into a list\n", "# mylist = iter((1, 2, 3, 4))\n", "# \n", "# print('mylist =', mylist)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is not possible to index an iterator." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;31m'tuple_iterator' object is not subscriptable\u001b[1;m\n" ] } ], "source": [ "# Try to index iterator\n", "try:\n", " myiter[0]\n", "except Exception as error:\n", " print_error(error)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead, iterator values are accessed one at a time using the `next` function." ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "next(myiter) = 1\n" ] } ], "source": [ "print('next(myiter) =', next(myiter))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Try re-running the previous cell multiple times to see what happens.\n", "\n", "### Built-In Functions\n", "\n", "Most built-in functions that work on lists (*e.g.* `sum`, `min`, `max`, *etc.*) will also work directly on iterators." ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sum(mylist) = 15\n", "sum(myiter) = 15\n" ] } ], "source": [ "# Create a list\n", "mylist = [1, 2, 3, 4, 5]\n", "\n", "# Create an iterator\n", "myiter = iter(mylist)\n", "\n", "print('sum(mylist) =', sum(mylist))\n", "print('sum(myiter) =', sum(myiter))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But note that, unlike a list, once an interator has been fully run it remains empty." ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sum(mylist) = 15\n", "sum(myiter) = 0\n" ] } ], "source": [ "print('sum(mylist) =', sum(mylist))\n", "print('sum(myiter) =', sum(myiter))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addtion, many built-in function return iterator-like objects by default (*e.g.* `zip`)." ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next: (1, 3)\n", "Next: (2, 4)\n" ] } ], "source": [ "# Create zip object\n", "myzip = zip([1, 2], [3, 4])\n", "\n", "print('Next:', next(myzip))\n", "print('Next:', next(myzip))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generators\n", "\n", "Generators can be thought of as functions that behave like iterators. \n", "\n", "Imagine you want a function that can provide the square of a list of values. One approach would be to write a function that takes all of the inputs and returns a list of the corresponding squared values. " ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Squared values = [1, 4, 9, 16, 25]\n" ] } ], "source": [ "# Function to calculate square of input values\n", "def square(values):\n", " \n", " return [value ** 2 for value in values]\n", "\n", "print('Squared values =', square([1, 2, 3, 4, 5]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, while this certainly works, we are storing all of the outputs in memory. In some situations we may only wish to calculate the output value when it's needed. To do so we can define a *generator* using a `yield` statement. " ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "next(mygen) = 1\n", "next(mygen) = 4\n", "next(mygen) = 9\n", "next(mygen) = 16\n", "next(mygen) = 25\n" ] } ], "source": [ "# Define a generator function to calculate square of input values\n", "def square(values):\n", " \n", " for value in values:\n", " \n", " yield value ** 2\n", " \n", "# Create a generator object\n", "mygen = square([1, 2, 3, 4, 5])\n", "\n", "# Run the generator\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case the function is only evaluated when the `next` function is called.\n", "\n", "An important feature of generators is that mutiple `yield` statements can be included in a single function." ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ready to work.\n", "Job's done.\n", "Alright.\n", "You're the king? Well, I didn't vote for you.\n" ] } ], "source": [ "# Define a generator function with multiple yields\n", "def peasant():\n", " \n", " yield \"Ready to work.\"\n", " yield \"Job's done.\"\n", " yield \"Alright.\"\n", " yield \"You're the king? Well, I didn't vote for you.\"\n", "\n", "# Create a generator object\n", "p = peasant()\n", "\n", "# Run the generator\n", "print(next(p))\n", "print(next(p))\n", "print(next(p))\n", "print(next(p))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generator Expressions\n", "\n", "Similarly to list comprehension, it is possible to produce generator objects in one line using `()`." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "next(mygen) = 1\n", "next(mygen) = 4\n", "next(mygen) = 9\n", "next(mygen) = 16\n", "next(mygen) = 25\n" ] } ], "source": [ "# Define a generator expression to square values from 1 to 5\n", "mygen = (value ** 2 for value in range(1, 6))\n", "\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))\n", "print('next(mygen) =', next(mygen))" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The sum of cubes from 0 to 9 is 2025.\n" ] } ], "source": [ "# Calculate the sum of cubes using a generator expression\n", "soc = sum((value ** 3 for value in range(10)))\n", "\n", "print('The sum of cubes from 0 to 9 is {}.'.format(soc))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Further Reading\n", "> - <a href=\"https://anandology.com/python-practice-book/iterators.html\" target=\"_blank\">Python Practice Book: Iterators</a>" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 8 Exercises\n", "---\n", "\n", "1. Create a list of your favourite superheros and another list of their secret identities.\n", " 1. Convert your two lists into a dictionary. (Can you do it in one line?)\n", " 1. Remove one of your heroes and add a villain to your dictionary.\n", " 1. Add a character that has multiple identities to your dictionary. (What kind of object should this be?)\n", " 1. Demonstrate that you can look up one of your character's identities.\n", " \n", "\n", "| Superhero | Identity |\n", "|:-----------:|:---------------------------:|\n", "| Iron Man | Tony Stark |\n", "| The Thing | Ben Grimm |\n", "| Storm | Ororo Munroe |\n", "| Spider-Man | Peter Parker, Miles Morales |\n", "\n", " \n" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "# Add your solution here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Use Hubble's law ($v=H_{0}\\,D$) to calculate the distance (in Mpc) of the galaxies in the following table.\n", "\n", "|Galaxy|Velocity (km/s)|\n", "|------|---------------|\n", "|NGC 123|1320|\n", "|NGC 2342|5690|\n", "|NGC 4442|8200|\n", "\n", "Remember that $H_0 \\approx 70$ km/s/Mpc." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Add your solution here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Flatten the following list using list comprehension.\n", "\n", "```python\n", "mylist = [[[1, 2], [3, 4, 5]], [[6], [7, 8]]]\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Add your solution here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4. Write a generator function that can be used to calculate the Fibonacci sequence.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Add your solution here" ] } ], "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.5" } }, "nbformat": 4, "nbformat_minor": 2 }