{ "metadata": { "name": "", "signature": "sha256:5a34df3616383602b8e2538d7fcb0a21c14cccec73dcc2e48fd31933e718579a" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Hands-on: Python Fundamentals -- Tuples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Objectives:**\n", "\n", "Upon completion of this lesson, you should be able to:\n", "\n", "* Describe the characteristics of the `tuple` in Python\n", "\n", "* Perform basic operations with tuples including creation, concatenation, repetition, slicing, and traversing\n", "\n", "* Get an idea in which situations tuples are and should be used" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "The tuple data structure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* In Python, a **tuple** is an immutable sequence of values\n", "\n", "* Each value in the tuple is an element or item\n", "\n", "* Elements can be any Python data type\n", "\n", "* Tuples can mix data types\n", "\n", "* Elements can be nested tuples\n", "\n", "* **Essentially tuples are immutable lists**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Microquiz**\n", "\n", "So what does it mean to be *immutable*? Which immutable type(s) do you already know?\n", "- - -" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Creating tuples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "* \n", "You create tuples like you would lists, but with parentheses instead\n", "of brackets" ] }, { "cell_type": "code", "collapsed": false, "input": [ "numbers = (1, 2, 3, 4)\n", "print numbers" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "cheeses = ('swiss', 'cheddar',\n", " 'ricotta', 'gouda')\n", "\n", "print cheeses" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Note one difference, that tuples with only a single item without following \",\" reduce to that item" ] }, { "cell_type": "code", "collapsed": false, "input": [ "t2 = ('a')\n", "print t2, type(t1)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Microquiz**\n", "\n", "Can you give an explanation why it is so?\n", "- - -" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Also note that you can create tuples by omitting surrounding parantheses:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "t1 = 1, \n", "print t1" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "t2 = 1, 2\n", "print t2" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and a trailing `,` would not have an additional effect:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "t2 = 1, 2,\n", "print t2" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Microquiz**\n", "\n", "Can you now exlain how magical\n", "\n", "```python\n", "a, b = b, a\n", "```\n", "\n", "was \"working\"?\n", "- - -" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* So to create a tuple with a single item, specify \",\" after the item:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "t2 = 'a', # valid, but not recommended\n", "print t2, type(t2)\n", "\n", "t2 = ('a',) # more verbose, non-ambigous specification\n", "print t2, type(t2)\n", "\n", "t3 = tuple('a')\n", "print t3, type(t3)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Nanoquiz**\n", "\n", "1. What would be the result of \n", "```python\n", "(None,)\n", "```\n", "?\n", "\n", "2. Would\n", "```python\n", "tuple(1)\n", "```\n", "work?\n", "- - -" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* You can create an empty tuple simply with" ] }, { "cell_type": "code", "collapsed": false, "input": [ "empty1 = ()\n", "empty2 = tuple()\n", "\n", "print empty1, empty2, empty1 is empty2, empty1 == empty2" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which also shows that empty tuple is kinda unique." ] }, { "cell_type": "code", "collapsed": false, "input": [ "() is ()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Creating tuples from iterables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* You can create tuples from any iterable by providing it to the `tuple()`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "alist = [1, 2, 3, 4]\n", "tuple(alist)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "tuple(range(1, 5))" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "astr = 'parrot'\n", "tuple(astr)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "tuple(\"subj%d\" % d for d in range(1, 4))" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note**: Remember list comprehensions from the previous class? There is no dedicated tuple comprehension, but you could try running\n", "\n", "```(\"subj%d\" % d for d in range(1, 4))```\n", "\n", "to get a glimpse into one of our future topics -- `generators`" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Tuple indexing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "* \n", "Just like other sequences, elements within a tuple are indexed" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cheeses[0]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* But the main difference of tuples from lists is that **Tuples are *immutable* **" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cheeses[0] = 'Feta'" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Microquiz**\n", "\n", "What was another immutable sequence type in Python we have studied already?\n", "- - -" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Slicing a tuple" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "* \n", "Like other sequences, tuples can be sliced" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cheeses[1:4]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Slicing a tuple creates a new tuple. It does not change the original tuple." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Using the + operator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "* \n", "You can use operators to manipulate tuples\n", "\n", "\n", "* \n", "The \n", "**+** operator returns a new tuple that is a concatenation of two tuples" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = (1, 2, 3)\n", "b = (4, 5, 6)\n", "c = a + b\n", "\n", "print a, b, c" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Repeating with `*`" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a*3" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Operations on tuples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Tuples support all the standard sequence operations, including:\n", "\n", " * Membership tests (using the **in** keyword)\n", " \n", " * Comparison (element-wise)\n", " \n", " * Iteration (e.g., in a for loop)\n", "\n", " * Concatenation and repetition\n", "\n", " * The `len()` function" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Comparisons" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Docs (https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange) say:\n", "\n", " Sequence types also support comparisons. In particular, tuples and lists are compared lexicographically by comparing corresponding elements. This means that to compare equal, every element must compare equal and the two sequences must be of the same type and have the same length.\n", "\n", "Also this:\n", "\n", " Tuples and lists are compared lexicographically using comparison of corresponding elements. This means that to compare equal, each element must compare equal and the two sequences must be of the same type and have the same length.\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "(3, 4) < (3, 5)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "(3, 4) < (2, 5)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Tuples and functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Remember that a function can only return one value\n", "\n", "* That is why many Python functions return tuples to \"pack\" multiple return values into it" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Consider for example this simple min_max function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def min_max(t):\n", " \"\"\"Returns the smallest and largest elements of a sequence as a tuple\"\"\"\n", " return min(t), max(t)\n", "\n", "seq = [12, 98, 23, 74, 3, 54]\n", "print min_max(seq)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which we can \"unpack\" into variables right away:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "min_v, max_v = min_max(seq)\n", "print min_v, max_v" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Excercises**\n", "\n", "1. What other sequences above min_max would be applicable to? Try with other types\n", "\n", "2. What will happen if you try to unpack to smaller or larger number of variables?\n", "- - -" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Positional arguments to the function are passed as a tuple" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* A parameter name that begins with * gathers all the arguments into a tuple\n", "\n", "* This allows functions to take a variable number of arguments" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def print_all(*args):\n", " print args\n", "\n", "print_all(1, 2.0, 'three')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and here is a bit more elaborate case, when `*args` are complimenting the main set of argument(s):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def formatted_msg(msg, *args):\n", " if args[0] < 10000000 and \"Hawai\" in str(args[1:]):\n", " print \"Stop dreaming\"\n", " else:\n", " print msg % args\n", "\n", "formatted_msg(\"If I had a %d bucks, I would have got %s, %s and %s\", 1000000, \"moved to Hawai\", \"bought kids a yaht\", \"sent them away\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Nanoexcercise**\n", "\n", "\"Make it happen\" in the above code example ;)" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Using tuple assignment in a for loop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "* \n", "Tuples can be very handy when looping\n", "\n", "\n", "* \n", "You can assign each value of a tuple to a separate variable" ] }, { "cell_type": "code", "collapsed": false, "input": [ "t = [('a', 0), ('b', 1), ('c', 2)]\n", "for letter, number in t:\n", " print number, letter" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Remember `zip` function? It is usually used to provide synchronized traversing of multiple iterables (lists, strings, ..), and used in conjunction with \"tuple packing\":" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def has_match(t1, t2):\n", " for x, y in zip(t1, t2):\n", " if x == y:\n", " return True\n", " return False\n", "\n", "a = [5, 4, 9, 7, 10]\n", "b = [4, 3, 5, 7, 15]\n", "print has_match(a, b)\n", "print has_match(\"abcabc\",\n", " \"cbacba\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Tuples vs Lists -- when to use which?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "http://stackoverflow.com/a/16941245\n", "\n", "Raymond Hettinger (one of the Python core developers) had this to say about tuples in a recent tweet:\n", "\n", "```\n", "#python tip:Generally, lists are for looping; tuples for structs. Lists are homogeneous; tuples heterogeneous. Lists for variable length.\n", "```\n", "\n", "\n", "`+` tuples can serve as \"keys\" for dictionaries (next lecture), lists -- can't" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Extra for hungry minds: namedtuple" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because tuples---as recommended above by Raymond himself---are often used for storing heterogeneous records, `namedtuple` was introduced to provide tuples where entries could have names. You can get a glimpse by simply running\n", "```python\n", "from collections import namedtuple\n", "namedtuple?\n", "```\n", "in IPython shell or checking out https://docs.python.org/2/library/collections.html#collections.namedtuple" ] } ], "metadata": {} } ] }