{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Defining New Classes in Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Classes are quite different in python than in other languages. One\n", "way in which they are different is that you can add attributes, or\n", "member variables and function, any time, even long after the class is\n", "defined. \n", "\n", "Say we want to define a class to hold a node in a search tree. \n", "The simplest class definition is" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Node:\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python uses `pass/ `or an empty statement or body.\n", "\n", "This class has no attributes, right? Watch this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n = Node()\n", "n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n.a = 42" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n.a" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n.a = 43\n", "n.a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simply assigning a value to something that looks like you are\n", "accessing a member variable creates it, but only for that instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "one = Node()\n", "two = Node()\n", "one.x = 42" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "one.x" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "two.x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can assign a new class attribute, though." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Node.cx = 'node class'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "one.cx" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "two.cx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, python is very flexible, too flexible some say. We really should\n", "define the attributes in class methods, like the constructor. The\n", "constructor for a class has the special name `__init__`. \n", "\n", "Here is how we should define our `Node` class. Let's say it should\n", "hold a `state`, and values for `h`, `g`, and `f`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Node:\n", " def __init__(self, state, f,g,h):\n", " self.state = state\n", " self.f = f\n", " self.g = g\n", " self.h = h" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = Node([1, 2, 3], 0, 0, 0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a.f" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The form that is printed when you evaluate it is kind of ugly. In\n", "python, there are two kinds of `toString` type of methods, for two\n", "purposes:\n", " * `__repr__` is meant to display a valid python expression that could be used to generate the value\n", " * `__str__` is meant to display a more human-oriented string that is not meant to be valid python code.\n", "Sometimes the `__repr__` result is good enough for humans, too.\n", "\n", "Here is an example for our `Node` class." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Node:\n", " \n", " def __init__(self, state, f, g, h):\n", " self.state = state\n", " self.f = f\n", " self.g = g\n", " self.h = h\n", " \n", " def __repr__(self):\n", " return 'Node({}, {}, {}, {})'.format(self.state, self.f, self.g, self.h)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = Node([1, 2, 3], 0, 0, 0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can define default values in the constructor, too. This allows `f`, `g`, and `h` to be entered as keyword arguments. And, therefore, the `__repr__` form becomes even more readable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Node:\n", " \n", " def __init__(self, state, f=0, g=0, h=0):\n", " self.state = state\n", " self.f = f\n", " self.g = g\n", " self.h = h\n", " \n", " def __repr__(self):\n", " return 'Node({}, f={}, g={}, h={})'.format(self.state, self.f, self.g, self.h)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = Node([1, 2, 3], 0, 0, 0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = Node([3,2,3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Sorting Lists" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sorting a list is easy. `sorted` produces a new list that is sorted. The `sort` method destructively sorts the list." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nums = [5, 2, 44, 8, 322, 54, 22]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "numsSorted = sorted(nums)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "numsSorted" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nums" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nums.sort()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nums" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But, what if the things we are sorting are structured and you want to sort by just one or some of the values? Say you have a list of tuples and want to sort by the second value? The `sorted` and `sort` functions take a `key` argument whose value is a function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs = [('a',54), ('b',52), ('c', 2), ('d', 21), ('e', 31)]\n", "pairs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sorted(pairs, key = lambda p: p[1])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs.sort(key=lambda p: p[1])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hey, how about sorting nodes??? Here is list of unexpanded nodes, maybe from someplace in the middle of an A* search." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "unExpanded = [Node([3,2,1],2,1,1),\n", " Node([2,1,3],4,2,2),\n", " Node([3,1,2],3,1,2),\n", " Node([1,3,2],1,1,0)]\n", "unExpanded" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What do we want to order them by? How would you do this in python?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "unExpanded.sort()\n", "unExpanded" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hummm.....nope. How about" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "unExpanded.sort(key=lambda n: n.f)\n", "unExpanded" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's better. Now we can get the lowest-f node by `unExpanded[0]` or get and remove it by `unExpanded.pop(0)`. We can also get the second-lowest f node by `unExpanded[1]`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best = unExpanded[0]\n", "best.f" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best.state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Conditional Expressions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The multiple lines of an if-else block can be written more compactly, and some might say more intuitively. See [this PEP on conditional expressions](http://docs.python.org/whatsnew/2.5.html). (Hey, what does PEP stand for?)\n", "\n", "What happens when you try to index beyond the end of a list?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "stuff = ['a', 'c', 'x']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "stuff[0]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "stuff[2]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "stuff[3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we should surround cases like this with `try-except` blocks. But, what if we just want an empty list if our index is beyond the end?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i = 4" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if i < len(stuff):\n", " result = stuff[i]\n", "else:\n", " result = []\n", " \n", "result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's a bit clunky. Conditional expressions to the rescue." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = stuff[i] if i < len(stuff) else []\n", "result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first expression is not evaluated if the `if` condition is false." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Arrays, from numpy module" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are going to play with some robot movement problems where the robot can move in discrete steps across the floor. To represent a bird's-eye view of the world, let's use an array.\n", "\n", "The `numpy` module in python is an efficient implementation of arrays. Let's create a 4x4 array of characters to represent a world in which the robot can be in 16 different positions. The position of the robot is marked with 'r' and every other element is a blank." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "world = np.array([\n", " [' ', ' ', ' ', ' '],\n", " [' ', 'r', ' ', ' '],\n", " [' ', ' ', ' ', ' '],\n", " [' ', ' ', ' ', ' ']])\n", "world" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How can we move the robot down one step? Index into the array with two indices. But first, here is a cool python idiom for swapping values." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = 42\n", "y = 100\n", "(x, y)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = y, x" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, the 'down' step can be done by" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "world" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "world[2,1], world[1,1] = world[1,1], world[2,1]\n", "world" ] } ], "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.6.5" } }, "nbformat": 4, "nbformat_minor": 1 }