{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Ch.7: Introduction to classes\n", "\n", " **Hans Petter Langtangen**, Simula Research Laboratory and University of Oslo, Dept. of Informatics\n", "\n", "Date: **Aug 15, 2015**\n", "\n", "# Basics of classes\n", "\n", "\n", "\n", "\n", "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Class = functions + data (variables) in one unit\n", "\n", " * A class packs together data (a collection of variables) and functions as *one single unit*\n", "\n", " * As a programmer you can create a new class and thereby a new object type (like `float`, `list`, `file`, ...)\n", "\n", " * A class is much like a module: a collection of \"global\" variables and functions that belong together\n", "\n", " * There is only one instance of a module while a class can have many instances (copies)\n", "\n", " * Modern programming applies classes to a large extent\n", "\n", " * It will take some time to master the class concept\n", "\n", " * Let's learn by doing!\n", "\n", "\n", "\n", "\n", "## Representing a function by a class; background\n", "\n", "Consider a function of $t$ with a parameter $v_0$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "y(t; v_0)=v_0t - {1\\over2}gt^2\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need both $v_0$ and $t$ to evaluate $y$ (and $g=9.81$), but\n", "how should we implement this?\n", "\n", "**Having $t$ and $v_0$ as arguments:**" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def y(t, v0):\n", " g = 9.81\n", " return v0*t - 0.5*g*t**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Having $t$ as argument and $v_0$ as global variable:**" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def y(t):\n", " g = 9.81\n", " return v0*t - 0.5*g*t**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Motivation: $y(t)$ is a function of $t$ only\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Representing a function by a class; idea\n", "\n", " * With a class, `y(t)` can be a function of `t` only, but still have\n", "\n", " `v0` and `g` as parameters with given values.\n", " * The class packs together a function `y(t)` and data (`v0`, `g`)\n", "\n", "\n", "\n", "## Representing a function by a class; technical overview\n", "\n", " * We make a class `Y` for $y(t;v_0)$ with variables `v0` and `g`\n", " and a function `value(t)` for computing $y(t;v_0)$\n", "\n", " * Any class should also have a function `__init__` for initialization of the variables\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Representing a function by a class; the code" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Y:\n", " def __init__(self, v0):\n", " self.v0 = v0\n", " self.g = 9.81\n", "\n", " def value(self, t):\n", " return self.v0*t - 0.5*self.g*t**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Usage:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y = Y(v0=3) # create instance (object)\n", "v = y.value(0.1) # compute function value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Representing a function by a class; the constructor\n", "\n", "When we write" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y = Y(v0=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we create a new variable (instance) `y` of type `Y`.\n", "`Y(3)` is a call to the *constructor*:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [], "source": [ " def __init__(self, v0):\n", " self.v0 = v0\n", " self.g = 9.81" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What is this `self` variable? Stay cool - it will be understood later as you get used to it\n", "\n", " * Think of `self` as `y`, i.e., the new variable to be created. `self.v0 = ...` means that we attach a variable `v0` to `self` (`y`).\n", "\n", " * `Y(3)` means `Y.__init__(y, 3)`, i.e., set `self=y`, `v0=3`\n", "\n", " * Remember: `self` is always first parameter in a function, but never inserted in the call!\n", "\n", " * After `y = Y(3)`, `y` has two variables `v0` and `g`" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print y.v0\n", "print y.g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> *In mathematics you\n", "> don't understand things. You just get used to them.*\n", "> John von Neumann, mathematician, 1903-1957.\n", "\n", "\n", "\n", "## Representing a function by a class; the value method\n", "\n", " * Functions in classes are called *methods*\n", "\n", " * Variables in classes are called *attributes*\n", "\n", "Here is the `value` method:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def value(self, t):\n", " return self.v0*t - 0.5*self.g*t**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Example on a call:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = y.value(t=0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`self` is left out in the call, but Python automatically inserts `y` as the `self` argument inside the `value` method. Think of the call as" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Y.value(y, t=0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inside `value` things \"appear\" as" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "return y.v0*t - 0.5*y.g*t**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`self` gives access to \"global variables\" in the class object.\n", "\n", "\n", "\n", "## Representing a function by a class; summary\n", "\n", " * Class `Y` collects the attributes `v0` and `g` and the method `value` as one unit\n", "\n", " * `value(t)` is function of `t` only, but has automatically access to the parameters `v0` and `g` as `self.v0` and `self.g`\n", "\n", " * The great advantage: we can send `y.value` as an ordinary function of `t` to any other function that expects a function `f(t)` of one variable" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def make_table(f, tstop, n):\n", " for t in linspace(0, tstop, n):\n", " print t, f(t)\n", "\n", "def g(t):\n", " return sin(t)*exp(-t)\n", "\n", "table(g, 2*pi, 101) # send ordinary function\n", "\n", "y = Y(6.5)\n", "table(y.value, 2*pi, 101) # send class method" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Representing a function by a class; the general case\n", "\n", "Given a function with $n+1$ parameters and one independent variable," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "f(x; p_0,\\ldots,p_n)\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "it is wise to represent `f` by a class where\n", "$p_0,\\ldots,p_n$ are attributes and where there is a method, say `value(self, x)`, for computing $f(x)$" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class MyFunc:\n", " def __init__(self, p0, p1, p2, ..., pn):\n", " self.p0 = p0\n", " self.p1 = p1\n", " ...\n", " self.pn = pn\n", "\n", " def value(self, x):\n", " return ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for a function with four parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "v(r; \\beta, \\mu_0, n, R) = \\left({\\beta\\over 2\\mu_0}\\right)^{{1\\over n}}\n", "{n \\over n+1}\\left( R^{1 + {1\\over n}} - r^{1 + {1\\over n}}\\right)\n", "$$" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class VelocityProfile:\n", " def __init__(self, beta, mu0, n, R):\n", " self.beta, self.mu0, self.n, self.R = \\\n", " beta, mu0, n, R\n", "\n", " def value(self, r):\n", " beta, mu0, n, R = \\\n", " self.beta, self.mu0, self.n, self.R\n", " n = float(n) # ensure float divisions\n", " v = (beta/(2.0*mu0))**(1/n)*(n/(n+1))*\\\n", " (R**(1+1/n) - r**(1+1/n))\n", " return v\n", "\n", "v = VelocityProfile(R=1, beta=0.06, mu0=0.02, n=0.1)\n", "print v.value(r=0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Rough sketch of a general Python class" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class MyClass:\n", " def __init__(self, p1, p2):\n", " self.attr1 = p1\n", " self.attr2 = p2\n", "\n", " def method1(self, arg):\n", " # can init new attribute outside constructor:\n", " self.attr3 = arg\n", " return self.attr1 + self.attr2 + self.attr3\n", "\n", " def method2(self):\n", " print 'Hello!'\n", "\n", "m = MyClass(4, 10)\n", "print m.method1(-2)\n", "m.method2()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is common to have a constructor where attributes are initialized, but this is not a requirement - attributes can be defined whenever desired\n", "\n", "\n", "\n", "## You can learn about other versions and views of class Y in the course book\n", "\n", " * The book features a section on a different version of class `Y` where there is no constructor (which is possible)\n", "\n", " * The book also features a section on how to implement classes without using classes\n", "\n", " * These sections may be clarifying - or confusing\n", "\n", "\n", "\n", "\n", "## But what is this self variable? I want to know now!\n", "\n", "**Warning.**\n", "\n", "You have two choices:\n", "\n", "1. follow the detailed explanations of what `self ` really is\n", "\n", "2. postpone understanding `self` until you have much more\n", " experience with class programming (suddenly `self` becomes clear!)\n", "\n", "\n", "\n", "The syntax" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y = Y(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "can be thought of as" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Y.__init__(y, 3) # class prefix Y. is like a module prefix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "self.v0 = v0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is actually" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y.v0 = 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How `self` works in the `value` method" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = y.value(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "can alternatively be written as" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = Y.value(y, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, when we do `instance.method(arg1, arg2)`, `self` becomes\n", "`instance` inside `method`.\n", "\n", "\n", "\n", "## Working with multiple instances may help explain `self`\n", "\n", "`id(obj)`: print unique Python identifier of an object" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class SelfExplorer:\n", " \"\"\"Class for computing a*x.\"\"\"\n", " def __init__(self, a):\n", " self.a = a\n", " print 'init: a=%g, id(self)=%d' % (self.a, id(self))\n", "\n", " def value(self, x):\n", " print 'value: a=%g, id(self)=%d' % (self.a, id(self))\n", " return self.a*x" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s1 = SelfExplorer(1)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [], "source": [ "id(s1)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s2 = SelfExplorer(2)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [], "source": [ "id(s2)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s1.value(4)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [], "source": [ "SelfExplorer.value(s1, 4)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s2.value(5)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [], "source": [ "SelfExplorer.value(s2, 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## But what is this self variable? I want to know now!\n", "\n", "**Warning.**\n", "\n", "You have two choices:\n", "\n", "1. follow the detailed explanations of what `self ` really is\n", "\n", "2. postpone understanding `self` until you have much more\n", " experience with class programming (suddenly `self` becomes clear!)\n", "\n", "\n", "\n", "The syntax" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y = Y(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "can be thought of as" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Y.__init__(y, 3) # class prefix Y. is like a module prefix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [], "source": [ "self.v0 = v0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is actually" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y.v0 = 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How `self` works in the `value` method" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = y.value(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "can alternatively be written as" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = Y.value(y, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, when we do `instance.method(arg1, arg2)`, `self` becomes\n", "`instance` inside `method`.\n", "\n", "\n", "\n", "## Working with multiple instances may help explain `self`\n", "\n", "`id(obj)`: print unique Python identifier of an object" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class SelfExplorer:\n", " \"\"\"Class for computing a*x.\"\"\"\n", " def __init__(self, a):\n", " self.a = a\n", " print 'init: a=%g, id(self)=%d' % (self.a, id(self))\n", "\n", " def value(self, x):\n", " print 'value: a=%g, id(self)=%d' % (self.a, id(self))\n", " return self.a*x" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s1 = SelfExplorer(1)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [], "source": [ "id(s1)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s2 = SelfExplorer(2)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [], "source": [ "id(s2)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s1.value(4)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [], "source": [ "SelfExplorer.value(s1, 4)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s2.value(5)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false }, "outputs": [], "source": [ "SelfExplorer.value(s2, 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Another class example: a bank account\n", "\n", " * Attributes: name of owner, account number, balance\n", "\n", " * Methods: deposit, withdraw, pretty print" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Account:\n", " def __init__(self, name, account_number, initial_amount):\n", " self.name = name\n", " self.no = account_number\n", " self.balance = initial_amount\n", "\n", " def deposit(self, amount):\n", " self.balance += amount\n", "\n", " def withdraw(self, amount):\n", " self.balance -= amount\n", "\n", " def dump(self):\n", " s = '%s, %s, balance: %s' % \\\n", " (self.name, self.no, self.balance)\n", " print s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## UML diagram of class Account\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Example on using class Account" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a1 = Account('John Olsson', '19371554951', 20000)\n", "a2 = Account('Liz Olsson', '19371564761', 20000)\n", "a1.deposit(1000)\n", "a1.withdraw(4000)\n", "a2.withdraw(10500)\n", "a1.withdraw(3500)\n", "print \"a1's balance:\", a1.balance" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a1.dump()" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a2.dump()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use underscore in attribute names to avoid misuse\n", "\n", "**Possible, but not intended use:**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**The assumptions on correct usage:**\n", "\n", " * The attributes should *not* be changed!\n", "\n", " * The `balance` attribute can be viewed\n", "\n", " * Changing `balance` is done through `withdraw` or `deposit`\n", "\n", "\n", "\n", "**Remedy:**\n", "\n", "Attributes and methods not intended for use outside the class can be marked as *protected* by prefixing the name with an underscore (e.g., `_name`). This is just a convention - and no technical way of avoiding attributes and methods to be accessed.\n", "\n", "\n", "\n", "## Improved class with attribute protection (underscore)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class AccountP:\n", " def __init__(self, name, account_number, initial_amount):\n", " self._name = name\n", " self._no = account_number\n", " self._balance = initial_amount\n", "\n", " def deposit(self, amount):\n", " self._balance += amount\n", "\n", " def withdraw(self, amount):\n", " self._balance -= amount\n", "\n", " def get_balance(self): # NEW - read balance value\n", " return self._balance\n", "\n", " def dump(self):\n", " s = '%s, %s, balance: %s' % \\\n", " (self._name, self._no, self._balance)\n", " print s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Usage of improved class AccountP" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a1 = AccountP('John Olsson', '19371554951', 20000)\n", "a1.withdraw(4000)\n", "\n", "print a1._balance # it works, but a convention is broken\n", "\n", "print a1.get_balance() # correct way of viewing the balance\n", "\n", "a1._no = '19371554955' # this is a \"serious crime\"!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Another example: a phone book\n", "\n", " * A phone book is a list of data about persons\n", "\n", " * Data about a person: name, mobile phone, office phone, private phone, email\n", "\n", " * Let us create a class for data about a person!\n", "\n", " * Methods:\n", "\n", " * Constructor for initializing name, plus one or more other data\n", "\n", " * Add new mobile number\n", "\n", " * Add new office number\n", "\n", " * Add new private number\n", "\n", " * Add new email\n", "\n", " * Write out person data\n", "\n", "\n", "\n", "## UML diagram of class Person\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## Basic code of class Person" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "collapsed": false }, "outputs": [], "source": [ " class Person:\n", " def __init__(self, name,\n", " mobile_phone=None, office_phone=None,\n", " private_phone=None, email=None):\n", " self.name = name\n", " self.mobile = mobile_phone\n", " self.office = office_phone\n", " self.private = private_phone\n", " self.email = email\n", "\n", " def add_mobile_phone(self, number):\n", " self.mobile = number\n", "\n", " def add_office_phone(self, number):\n", " self.office = number\n", "\n", " def add_private_phone(self, number):\n", " self.private = number\n", "\n", " def add_email(self, address):\n", " self.email = address" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code of a dump method for printing all class contents" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "collapsed": false }, "outputs": [], "source": [ " class Person:\n", " ...\n", " def dump(self):\n", " s = self.name + '\\n'\n", " if self.mobile is not None:\n", " s += 'mobile phone: %s\\n' % self.mobile\n", " if self.office is not None:\n", " s += 'office phone: %s\\n' % self.office\n", " if self.private is not None:\n", " s += 'private phone: %s\\n' % self.private\n", " if self.email is not None:\n", " s += 'email address: %s\\n' % self.email\n", " print s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Usage:**" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p1 = Person('Hans Petter Langtangen', email='hpl@simula.no')\n", "p1.add_office_phone('67828283'),\n", "p2 = Person('Aslak Tveito', office_phone='67828282')\n", "p2.add_email('aslak@simula.no')\n", "phone_book = [p1, p2] # list\n", "phone_book = {'Langtangen': p1, 'Tveito': p2} # better\n", "for p in phone_book:\n", " p.dump()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Another example: a class for a circle\n", "\n", " * A circle is defined by its center point $x_0$, $y_0$ and its radius $R$\n", "\n", " * These data can be attributes in a class\n", "\n", " * Possible methods in the class: `area`, `circumference`\n", "\n", " * The constructor initializes $x_0$, $y_0$ and $R$" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Circle:\n", " def __init__(self, x0, y0, R):\n", " self.x0, self.y0, self.R = x0, y0, R\n", "\n", " def area(self):\n", " return pi*self.R**2\n", "\n", " def circumference(self):\n", " return 2*pi*self.R" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "collapsed": false }, "outputs": [], "source": [ "c = Circle(2, -1, 5)\n", "print 'A circle with radius %g at (%g, %g) has area %g' % \\\n", " (c.R, c.x0, c.y0, c.area())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test function for class Circle" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def test_Circle():\n", " R = 2.5\n", " c = Circle(7.4, -8.1, R)\n", "\n", " from math import pi\n", " expected_area = pi*R**2\n", " computed_area = c.area()\n", " diff = abs(expected_area - computed_area)\n", " tol = 1E-14\n", " assert diff < tol, 'bug in Circle.area, diff=%s' % diff\n", "\n", " expected_circumference = 2*pi*R\n", " computed_circumference = c.circumference()\n", " diff = abs(expected_circumference - computed_circumference)\n", " assert diff < tol, 'bug in Circle.circumference, diff=%s' % diff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Special methods" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class MyClass:\n", " def __init__(self, a, b):\n", " ...\n", "\n", "p1 = MyClass(2, 5)\n", "p2 = MyClass(-1, 10)\n", "\n", "p3 = p1 + p2\n", "p4 = p1 - p2\n", "p5 = p1*p2\n", "p6 = p1**7 + 4*p3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "## *Special methods* allow nice syntax and are recognized by double leading and trailing underscores" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def __init__(self, ...)\n", "def __call__(self, ...)\n", "def __add__(self, other)\n", "\n", "# Python syntax\n", "y = Y(4)\n", "print y(2)\n", "z = Y(6)\n", "print y + z\n", "\n", "# What's actually going on\n", "Y.__init__(y, 4)\n", "print Y.__call__(y, 2)\n", "Y.__init__(z, 6)\n", "print Y.__add__(y, z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We shall learn about many more such *special methods*\n", "\n", "\n", "\n", "## Example on a call special method\n", "\n", "Replace the `value` method by a *call* special method:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Y:\n", " def __init__(self, v0):\n", " self.v0 = v0\n", " self.g = 9.81\n", "\n", " def __call__(self, t):\n", " return self.v0*t - 0.5*self.g*t**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can write" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y = Y(3)\n", "v = y(0.1) # same as v = y.__call__(0.1) or Y.__call__(y, 0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note:\n", "\n", " * The instance `y` behaves and looks as a function!\n", "\n", " * The `value(t)` method does the same, but `__call__` allows nicer syntax for computing function values\n", "\n", "\n", "\n", "## Representing a function by a class revisited\n", "\n", "Given a function with $n+1$ parameters and one independent variable," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "f(x; p_0,\\ldots,p_n)\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "it is wise to represent `f` by a class where $p_0,\\ldots,p_n$\n", "are attributes and `__call__(x)` computes $f(x)$" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class MyFunc:\n", " def __init__(self, p0, p1, p2, ..., pn):\n", " self.p0 = p0\n", " self.p1 = p1\n", " ...\n", " self.pn = pn\n", "\n", " def __call__(self, x):\n", " return ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Can we automatically differentiate a function?\n", "\n", "Given some mathematical function in Python, say" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def f(x):\n", " return x**3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "can we make a class `Derivative` and write" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dfdx = Derivative(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "so that `dfdx` behaves as a function that computes the derivative of `f(x)`?" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print dfdx(2) # computes 3*x**2 for x=2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic differentiation; solution\n", "\n", "**Method.**\n", "\n", "We use numerical differentiation \"behind the curtain\":" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "f'(x) \\approx {f(x+h)-f(x)\\over h}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "for a small (yet moderate) $h$, say $h=10^{-5}$\n", "\n", "\n", "\n", "**Implementation.**" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Derivative:\n", " def __init__(self, f, h=1E-5):\n", " self.f = f\n", " self.h = float(h)\n", "\n", " def __call__(self, x):\n", " f, h = self.f, self.h # make short forms\n", " return (f(x+h) - f(x))/h" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic differentiation; demo" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from math import *\n", "df = Derivative(sin)\n", "x = pi\n", "df(x)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cos(x) # exact" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def g(t):\n", " return t**3" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dg = Derivative(g)\n", "t = 1\n", "dg(t) # compare with 3 (exact)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic differentiation; useful in Newton's method\n", "\n", "Newton's method solves nonlinear equations $f(x)=0$, but the method requires $f'(x)$" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def Newton(f, xstart, dfdx, epsilon=1E-6):\n", " ...\n", " return x, no_of_iterations, f(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose $f'(x)$ requires boring/lengthy derivation, then class `Derivative` is handy:" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def f(x):\n", " return 100000*(x - 0.9)**2 * (x - 1.1)**3" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "collapsed": false }, "outputs": [], "source": [ "df = Derivative(f)\n", "xstart = 1.01\n", "Newton(f, xstart, df, epsilon=1E-5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic differentiation; test function\n", "\n", " * How can we test class `Derivative`?\n", "\n", " * Method 1: compute $(f(x+h)-f(x))/h$ by hand for some $f$ and $h$\n", "\n", " * Method 2: utilize that linear functions are differentiated\n", " exactly by our numerical formula, regardless of $h$\n", "\n", "Test function based on method 2:" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def test_Derivative():\n", " # The formula is exact for linear functions, regardless of h\n", " f = lambda x: a*x + b\n", " a = 3.5; b = 8\n", " dfdx = Derivative(f, h=0.5)\n", " diff = abs(dfdx(4.5) - a)\n", " assert diff < 1E-14, 'bug in class Derivative, diff=%s' % diff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic differentiation; explanation of the test function\n", "\n", "**Use of lambda functions:**" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "collapsed": false }, "outputs": [], "source": [ "f = lambda x: a*x + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is equivalent to" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def f(x):\n", " return a*x + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lambda functions are convenient for producing quick, short code\n", "\n", "\n", "\n", "**Use of closure:**" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "collapsed": false }, "outputs": [], "source": [ "f = lambda x: a*x + b\n", "a = 3.5; b = 8\n", "dfdx = Derivative(f, h=0.5)\n", "dfdx(4.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks straightforward...but\n", "\n", " * How can `Derivative.__call__` know `a` and `b` when it calls\n", " our `f(x)` function?\n", "\n", " * Local functions inside functions remember (have access to)\n", " *all* local variables in the function they are defined (!)\n", "\n", " * `f` can access `a` and `b` in `test_Derivative` even when\n", " called from `__call__` in class `Derivative\n", "\n", " * `f` is known as a *closure* in computer science\n", "\n", "\n", "\n", "\n", "## Automagic differentiation detour; sympy solution (exact differentiation via symbolic expressions)\n", "\n", "SymPy can perform exact, symbolic differentiation:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "collapsed": false }, "outputs": [], "source": [ ">>> from sympy import *\n", ">>> def g(t):\n", "... return t**3\n", "...\n", ">>> t = Symbol('t')\n", ">>> dgdt = diff(g(t), t) # compute g'(t)\n", ">>> dgdt\n", "3*t**2\n", "\n", ">>> # Turn sympy expression dgdt into Python function dg(t)\n", ">>> dg = lambdify([t], dgdt)\n", ">>> dg(1)\n", "3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic differentiation detour; class based on sympy" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import sympy as sp\n", "\n", "class Derivative_sympy:\n", " def __init__(self, f):\n", " # f: Python f(x)\n", " x = sp.Symbol('x')\n", " sympy_f = f(x)\n", " sympy_dfdx = sp.diff(sympy_f, x)\n", " self.__call__ = sp.lambdify([x], sympy_dfdx)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def g(t):\n", " return t**3" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def h(y):\n", " return sp.sin(y)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dg = Derivative_sympy(g)\n", "dh = Derivative_sympy(h)\n", "dg(1) # 3*1**2 = 3" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from math import pi\n", "dh(pi) # cos(pi) = -1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic integration; problem setting\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Given a function $f(x)$, we want to compute" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "F(x; a) = \\int_a^x f(t)dt\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic integration; technique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "F(x; a) = \\int_a^x f(t)dt\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Technique: Midpoint rule or Trapezoidal rule, here the latter:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\int_a^x f(t)dt = h\\left({1\\over2}f(a) + \\sum_{i=1}^{n-1} f(a+ih) + {1\\over2}f(x)\\right)\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Desired application code:" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def f(x):\n", " return exp(-x**2)*sin(10*x)\n", "\n", "a = 0; n = 200\n", "F = Integral(f, a, n)\n", "x = 1.2\n", "print F(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic integration; implementation" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def trapezoidal(f, a, x, n):\n", " h = (x-a)/float(n)\n", " I = 0.5*f(a)\n", " for i in range(1, n):\n", " I += f(a + i*h)\n", " I += 0.5*f(x)\n", " I *= h\n", " return I" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Class `Integral` holds `f`, `a` and `n` as attributes and has a call special method for computing the integral:" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Integral:\n", " def __init__(self, f, a, n=100):\n", " self.f, self.a, self.n = f, a, n\n", "\n", " def __call__(self, x):\n", " return trapezoidal(self.f, self.a, x, self.n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automagic integration; test function\n", "\n", " * How can we test class `Integral`?\n", "\n", " * Method 1: compute by hand for some $f$ and small $n$\n", "\n", " * Method 2: utilize that linear functions are integrated\n", " exactly by our numerical formula, regardless of $n$\n", "\n", "Test function based on method 2:" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def test_Integral():\n", " f = lambda x: 2*x + 5\n", " F = lambda x: x**2 + 5*x - (a**2 + 5*a)\n", " a = 2\n", " dfdx = Integralf, a, n=4)\n", " x = 6\n", " diff = abs(I(x) - (F(x) - F(a)))\n", " assert diff < 1E-15, 'bug in class Integral, diff=%s' % diff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Special method for printing\n", "\n", " * In Python, we can usually print an object `a` by `print a`,\n", " works for built-in types (strings, lists, floats, ...)\n", "\n", " * Python does not know how to print objects of a user-defined class,\n", " but if the class defines a method `__str__`, Python will use this method to convert an object to a string\n", "\n", "Example:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Y:\n", " ...\n", " def __call__(self, t):\n", " return self.v0*t - 0.5*self.g*t**2\n", "\n", " def __str__(self):\n", " return 'v0*t - 0.5*g*t**2; v0=%g' % self.v0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Demo:" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "collapsed": false }, "outputs": [], "source": [ "y = Y(1.5)\n", "y(0.2)" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for polynomials; functionality\n", "\n", "A polynomial can be specified by a list of its coefficients. For example,\n", "$1 - x^2 + 2x^3$ is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "1 + 0\\cdot x - 1\\cdot x^2 + 2\\cdot x^3\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and the coefficients can be stored as `[1, 0, -1, 2]`\n", "\n", "**Desired application code:**" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p1 = Polynomial([1, -1])\n", "print p1" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p2 = Polynomial([0, 1, 0, 0, -6, -1])\n", "p3 = p1 + p2\n", "print p3.coeff" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print p3" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p2.differentiate()\n", "print p2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How can we make class `Polynomial`?\n", "\n", "\n", "\n", "## Class Polynomial; basic code" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Polynomial:\n", " def __init__(self, coefficients):\n", " self.coeff = coefficients\n", "\n", " def __call__(self, x):\n", " s = 0\n", " for i in range(len(self.coeff)):\n", " s += self.coeff[i]*x**i\n", " return s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class Polynomial; addition" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Polynomial:\n", " ...\n", "\n", " def __add__(self, other):\n", " # return self + other\n", "\n", " # start with the longest list and add in the other:\n", " if len(self.coeff) > len(other.coeff):\n", " coeffsum = self.coeff[:] # copy!\n", " for i in range(len(other.coeff)):\n", " coeffsum[i] += other.coeff[i]\n", " else:\n", " coeffsum = other.coeff[:] # copy!\n", " for i in range(len(self.coeff)):\n", " coeffsum[i] += self.coeff[i]\n", " return Polynomial(coeffsum)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class Polynomial; multiplication\n", "\n", "**Mathematics:**\n", "\n", "Multiplication of two general polynomials:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\left(\\sum_{i=0}^Mc_ix^i\\right)\\left(\\sum_{j=0}^N d_jx^j\\right)\n", "= \\sum_{i=0}^M \\sum_{j=0}^N c_id_j x^{i+j}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The coeff. corresponding to power $i+j$ is $c_i\\cdot d_j$. The\n", "list `r` of coefficients of the result:\n", "`r[i+j] = c[i]*d[j]` (`i` and `j` running from\n", "0 to $M$ and $N$, resp.)\n", "\n", "\n", "\n", "**Implementation:**" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Polynomial:\n", " ...\n", " def __mul__(self, other):\n", " M = len(self.coeff) - 1\n", " N = len(other.coeff) - 1\n", " coeff = [0]*(M+N+1) # or zeros(M+N+1)\n", " for i in range(0, M+1):\n", " for j in range(0, N+1):\n", " coeff[i+j] += self.coeff[i]*other.coeff[j]\n", " return Polynomial(coeff)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class Polynomial; differentation\n", "\n", "**Mathematics:**\n", "\n", "Rule for differentiating a general polynomial:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "{d\\over dx}\\sum_{i=0}^n c_ix^i = \\sum_{i=1}^n ic_ix^{i-1}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `c` is the list of coefficients, the derivative has a list\n", "of coefficients, `dc`, where `dc[i-1] = i*c[i]` for\n", "`i` running from 1 to the largest index in `c`.\n", "Note that `dc` has one element less than `c`.\n", "\n", "\n", "\n", "**Implementation:**" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Polynomial:\n", " ...\n", " def differentiate(self): # change self\n", " for i in range(1, len(self.coeff)):\n", " self.coeff[i-1] = i*self.coeff[i]\n", " del self.coeff[-1]\n", "\n", " def derivative(self): # return new polynomial\n", " dpdx = Polynomial(self.coeff[:]) # copy\n", " dpdx.differentiate()\n", " return dpdx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class Polynomial; pretty print" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Polynomial:\n", " ...\n", " def __str__(self):\n", " s = ''\n", " for i in range(0, len(self.coeff)):\n", " if self.coeff[i] != 0:\n", " s += ' + %g*x^%d' % (self.coeff[i], i)\n", " # fix layout (lots of special cases):\n", " s = s.replace('+ -', '- ')\n", " s = s.replace(' 1*', ' ')\n", " s = s.replace('x^0', '1')\n", " s = s.replace('x^1 ', 'x ')\n", " s = s.replace('x^1', 'x')\n", " if s[0:3] == ' + ': # remove initial +\n", " s = s[3:]\n", " if s[0:3] == ' - ': # fix spaces for initial -\n", " s = '-' + s[3:]\n", " return s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for polynomials; usage\n", "\n", "Consider" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "p_1(x)= 1-x,\\quad p_2(x)=x - 6x^4 - x^5\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and their sum" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "p_3(x) = p_1(x) + p_2(x) = 1 -6x^4 - x^5\n", "$$" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p1 = Polynomial([1, -1])\n", "print p1" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p2 = Polynomial([0, 1, 0, 0, -6, -1])\n", "p3 = p1 + p2\n", "print p3.coeff" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p2.differentiate()\n", "print p2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The programmer is in charge of defining special methods!\n", "\n", "How should, e.g., `__add__(self, other)` be defined? This is completely up to the programmer, depending on what is meaningful by `object1 + object2`.\n", "\n", "\n", "\n", "An anthropologist was asking a primitive tribesman about arithmetic. When the anthropologist asked, *What does two and two make?* the tribesman replied, *Five.* Asked to explain, the tribesman said, *If I have a rope with two knots, and another rope with two knots, and I join the ropes together, then I have five knots.*\n", "\n", "\n", "\n", "## Special methods for arithmetic operations" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "collapsed": false }, "outputs": [], "source": [ "c = a + b # c = a.__add__(b)\n", "\n", "c = a - b # c = a.__sub__(b)\n", "\n", "c = a*b # c = a.__mul__(b)\n", "\n", "c = a/b # c = a.__div__(b)\n", "\n", "c = a**e # c = a.__pow__(e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Special methods for comparisons" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a == b # a.__eq__(b)\n", "\n", "a != b # a.__ne__(b)\n", "\n", "a < b # a.__lt__(b)\n", "\n", "a <= b # a.__le__(b)\n", "\n", "a > b # a.__gt__(b)\n", "\n", "a >= b # a.__ge__(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for vectors in the plane\n", "\n", "**Mathematical operations for vectors in the plane:**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{align*}\n", "(a,b) + (c,d) &= (a+c, b+d)\\\\\n", "(a,b) - (c,d) &= (a-c, b-d)\\\\\n", "(a,b)\\cdot(c,d) &= ac + bd\\\\\n", "(a,b) &= (c, d)\\hbox{ if }a=c\\hbox{ and }b=d\n", "\\end{align*}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Desired application code:**" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "collapsed": false }, "outputs": [], "source": [ "u = Vec2D(0,1)\n", "v = Vec2D(1,0)\n", "print u + v" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = u + v\n", "w = Vec2D(1,1)\n", "a == w" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print u - v" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print u*v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for vectors; implementation" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "collapsed": false }, "outputs": [], "source": [ " class Vec2D:\n", " def __init__(self, x, y):\n", " self.x = x; self.y = y\n", "\n", " def __add__(self, other):\n", " return Vec2D(self.x+other.x, self.y+other.y)\n", "\n", " def __sub__(self, other):\n", " return Vec2D(self.x-other.x, self.y-other.y)\n", "\n", " def __mul__(self, other):\n", " return self.x*other.x + self.y*other.y\n", "\n", " def __abs__(self):\n", " return math.sqrt(self.x**2 + self.y**2)\n", "\n", " def __eq__(self, other):\n", " return self.x == other.x and self.y == other.y\n", "\n", " def __str__(self):\n", " return '(%g, %g)' % (self.x, self.y)\n", "\n", " def __ne__(self, other):\n", " return not self.__eq__(other) # reuse __eq__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The repr special method: `eval(repr(p))` creates `p`" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class MyClass:\n", " def __init__(self, a, b):\n", " self.a, self.b = a, b\n", "\n", " def __str__(self):\n", " \"\"\"Return string with pretty print.\"\"\"\n", " return 'a=%s, b=%s' % (self.a, self.b)\n", "\n", " def __repr__(self):\n", " \"\"\"Return string such that eval(s) recreates self.\"\"\"\n", " return 'MyClass(%s, %s)' % (self.a, self.b)" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "collapsed": false }, "outputs": [], "source": [ "m = MyClass(1, 5)\n", "print m # calls m.__str__()" ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "collapsed": false }, "outputs": [], "source": [ "str(m) # calls m.__str__()" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s = repr(m) # calls m.__repr__()\n", "s" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "collapsed": false }, "outputs": [], "source": [ "m2 = eval(s) # same as m2 = MyClass(1, 5)\n", "m2 # calls m.__repr__()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class Y revisited with repr print method" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Y:\n", " \"\"\"Class for function y(t; v0, g) = v0*t - 0.5*g*t**2.\"\"\"\n", "\n", " def __init__(self, v0):\n", " \"\"\"Store parameters.\"\"\"\n", " self.v0 = v0\n", " self.g = 9.81\n", "\n", " def __call__(self, t):\n", " \"\"\"Evaluate function.\"\"\"\n", " return self.v0*t - 0.5*self.g*t**2\n", "\n", " def __str__(self):\n", " \"\"\"Pretty print.\"\"\"\n", " return 'v0*t - 0.5*g*t**2; v0=%g' % self.v0\n", "\n", " def __repr__(self):\n", " \"\"\"Print code for regenerating this instance.\"\"\"\n", " return 'Y(%s)' % self.v0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for complex numbers; functionality\n", "\n", "Python already has a class `complex` for complex numbers, but implementing such a class is a good pedagogical example on class programming (especially with special methods).\n", "\n", "\n", "\n", "**Usage:**" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "collapsed": false }, "outputs": [], "source": [ "u = Complex(2,-1)\n", "v = Complex(1) # zero imaginary part\n", "w = u + v\n", "print w" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "collapsed": false }, "outputs": [], "source": [ "w != u" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "collapsed": false }, "outputs": [], "source": [ "u*v" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "collapsed": false }, "outputs": [], "source": [ "u < v" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print w + 4" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print 4 - w" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for complex numbers; implementation (part 1)" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Complex:\n", " def __init__(self, real, imag=0.0):\n", " self.real = real\n", " self.imag = imag\n", "\n", " def __add__(self, other):\n", " return Complex(self.real + other.real,\n", " self.imag + other.imag)\n", "\n", " def __sub__(self, other):\n", " return Complex(self.real - other.real,\n", " self.imag - other.imag)\n", "\n", " def __mul__(self, other):\n", " return Complex(self.real*other.real - self.imag*other.imag,\n", " self.imag*other.real + self.real*other.imag)\n", "\n", " def __div__(self, other):\n", " ar, ai, br, bi = self.real, self.imag, \\\n", " other.real, other.imag # short forms\n", " r = float(br**2 + bi**2)\n", " return Complex((ar*br+ai*bi)/r, (ai*br-ar*bi)/r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class for complex numbers; implementation (part 2)" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "collapsed": false }, "outputs": [], "source": [ " def __abs__(self):\n", " return sqrt(self.real**2 + self.imag**2)\n", "\n", " def __neg__(self): # defines -c (c is Complex)\n", " return Complex(-self.real, -self.imag)\n", "\n", " def __eq__(self, other):\n", " return self.real == other.real and \\\n", " self.imag == other.imag\n", "\n", " def __ne__(self, other):\n", " return not self.__eq__(other)\n", "\n", " def __str__(self):\n", " return '(%g, %g)' % (self.real, self.imag)\n", "\n", " def __repr__(self):\n", " return 'Complex' + str(self)\n", "\n", " def __pow__(self, power):\n", " raise NotImplementedError(\n", " 'self**power is not yet impl. for Complex')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Refining the special methods for arithmetics\n", "\n", "Can we add a real number to a complex number?" ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "collapsed": false }, "outputs": [], "source": [ "u = Complex(1, 2)\n", "w = u + 4.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Problem: we have assumed that `other` is `Complex`.\n", "Remedy:" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Complex:\n", " ...\n", " def __add__(self, other):\n", " if isinstance(other, (float,int)):\n", " other = Complex(other)\n", " return Complex(self.real + other.real,\n", " self.imag + other.imag)\n", "\n", "# or\n", "\n", " def __add__(self, other):\n", " if isinstance(other, (float,int)):\n", " return Complex(self.real + other, self.imag)\n", " else:\n", " return Complex(self.real + other.real,\n", " self.imag + other.imag)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Special methods for \"right\" operands; addition\n", "\n", "What if we try this:" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "collapsed": false }, "outputs": [], "source": [ "u = Complex(1, 2)\n", "w = 4.5 + u" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Problem: Python's `float` objects cannot add a `Complex`.\n", "\n", "Remedy: if a class has an `__radd__(self, other)` special method, Python applies this for `other + self`" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Complex:\n", " ...\n", " def __radd__(self, other):\n", " \"\"\"Rturn other + self.\"\"\"\n", " # other + self = self + other:\n", " return self.__add__(other)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Special methods for \"right\" operands; subtraction\n", "\n", "Right operands for subtraction is a bit more complicated since $a-b \\neq b-a$:" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Complex:\n", " ...\n", " def __sub__(self, other):\n", " if isinstance(other, (float,int)):\n", " other = Complex(other)\n", " return Complex(self.real - other.real,\n", " self.imag - other.imag)\n", "\n", " def __rsub__(self, other):\n", " if isinstance(other, (float,int)):\n", " other = Complex(other)\n", " return other.__sub__(self)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What's in a class?" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "collapsed": false }, "outputs": [], "source": [ " class A:\n", " \"\"\"A class for demo purposes.\"\"\"\n", " def __init__(self, value):\n", " self.v = value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Any instance holds its attributes in the `self.__dict__` dictionary (Python automatically creates this dict)" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = A([1,2])\n", "print a.__dict__ # all attributes" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dir(a) # what's in object a?" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a.__doc__ # programmer's documentation of A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ooops - we can add new attributes as we want!" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a.myvar = 10 # add new attribute (!)\n", "a.__dict__" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dir(a)" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "collapsed": false }, "outputs": [], "source": [ "b = A(-1)\n", "b.__dict__ # b has no myvar attribute" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dir(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary of defining a class\n", "\n", "**Example on a defining a class with attributes and methods:**" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "\n", "class Gravity:\n", " \"\"\"Gravity force between two objects.\"\"\"\n", " def __init__(self, m, M):\n", " self.m = m\n", " self.M = M\n", " self.G = 6.67428E-11 # gravity constant\n", "\n", " def force(self, r):\n", " G, m, M = self.G, self.m, self.M\n", " return G*m*M/r**2\n", "\n", " def visualize(self, r_start, r_stop, n=100):\n", " from scitools.std import plot, linspace\n", " r = linspace(r_start, r_stop, n)\n", " g = self.force(r)\n", " title='m=%g, M=%g' % (self.m, self.M)\n", " plot(r, g, title=title)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary of using a class\n", "\n", "**Example on using the class:**" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "collapsed": false }, "outputs": [], "source": [ "mass_moon = 7.35E+22\n", "mass_earth = 5.97E+24\n", "\n", "# make instance of class Gravity:\n", "gravity = Gravity(mass_moon, mass_earth)\n", "\n", "r = 3.85E+8 # earth-moon distance in meters\n", "Fg = gravity.force(r) # call class method" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary of special methods\n", "\n", " * `c = a + b` implies `c = a.__add__(b)`\n", "\n", " * There are special methods for `a+b`, `a-b`, `a*b`, `a/b`, `a**b`, `-a`, `if a:`, `len(a)`, `str(a)` (pretty print), `repr(a)` (recreate `a` with `eval`), etc.\n", "\n", " * With special methods we can create new mathematical objects like vectors, polynomials and complex numbers and write \"mathematical code\" (arithmetics)\n", "\n", " * The call special method is particularly handy: `v = c(5)` means `v = c.__call__(5)`\n", "\n", " * Functions with parameters should be represented by a class with the parameters as attributes and with a call special method for evaluating the function\n", "\n", "\n", "\n", "## Summarizing example: interval arithmetics for uncertainty quantification in formulas\n", "\n", "**Uncertainty quantification:**\n", "\n", "Consider measuring gravity $g$ by dropping a ball from $y=y_0$ to $y=0$\n", "in time $T$:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "g = 2y_0T^{-2}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What if $y_0$ and $T$ are uncertain? Say $y_0\\in [0.99,1.01]$ m\n", "and $T\\in [0.43, 0.47]$ s. What is the uncertainty in $g$?\n", "\n", "\n", "\n", "## The uncertainty can be computed by interval arithmetics\n", "\n", "**Interval arithmetics.**\n", "\n", "Rules for computing with intervals, $p=[a,b]$ and $q=[c,d]$:\n", "\n", " * $p+q = [a + c, b + d]$\n", "\n", " * $p-q = [a - d, b - c]$\n", "\n", " * $pq = [\\min(ac, ad, bc, bd), \\max(ac, ad, bc, bd)]$\n", "\n", " * $p/q = [\\min(a/c, a/d, b/c, b/d), \\max(a/c, a/d, b/c, b/d)]$\n", " ($[c,d]$ cannot contain zero)\n", "\n", "Obvious idea: make a class for interval arithmetics!\n", "\n", "\n", "\n", "## Class for interval arithmetics" ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class IntervalMath:\n", " def __init__(self, lower, upper):\n", " self.lo = float(lower)\n", " self.up = float(upper)\n", "\n", " def __add__(self, other):\n", " a, b, c, d = self.lo, self.up, other.lo, other.up\n", " return IntervalMath(a + c, b + d)\n", "\n", " def __sub__(self, other):\n", " a, b, c, d = self.lo, self.up, other.lo, other.up\n", " return IntervalMath(a - d, b - c)\n", "\n", " def __mul__(self, other):\n", " a, b, c, d = self.lo, self.up, other.lo, other.up\n", " return IntervalMath(min(a*c, a*d, b*c, b*d),\n", " max(a*c, a*d, b*c, b*d))\n", "\n", " def __div__(self, other):\n", " a, b, c, d = self.lo, self.up, other.lo, other.up\n", " if c*d <= 0: return None\n", " return IntervalMath(min(a/c, a/d, b/c, b/d),\n", " max(a/c, a/d, b/c, b/d))\n", " def __str__(self):\n", " return '[%g, %g]' % (self.lo, self.up)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demo of the new class for interval arithmetics\n", "\n", "**Code:**" ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "collapsed": false }, "outputs": [], "source": [ "I = IntervalMath # abbreviate\n", "a = I(-3,-2)\n", "b = I(4,5)\n", "\n", "expr = 'a+b', 'a-b', 'a*b', 'a/b' # test expressions\n", "for e in expr:\n", " print e, '=', eval(e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Output:**" ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a+b = [1, 3]\n", "a-b = [-8, -6]\n", "a*b = [-15, -8]\n", "a/b = [-0.75, -0.4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Shortcomings of the class\n", "\n", "This code" ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = I(4,5)\n", "q = 2\n", "b = a*q" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "leads to" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " File \"IntervalMath.py\", line 15, in __mul__\n", " a, b, c, d = self.lo, self.up, other.lo, other.up\n", " AttributeError: 'float' object has no attribute 'lo'\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Problem: `IntervalMath` times `int` is not defined.\n", "\n", "Remedy: (cf. class `Complex`)" ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class IntervalArithmetics:\n", " ...\n", " def __mul__(self, other):\n", " if isinstance(other, (int, float)): # NEW\n", " other = IntervalMath(other, other) # NEW\n", " a, b, c, d = self.lo, self.up, other.lo, other.up\n", " return IntervalMath(min(a*c, a*d, b*c, b*d),\n", " max(a*c, a*d, b*c, b*d))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(with similar adjustments of other special methods)\n", "\n", "\n", "\n", "## More shortcomings of the class\n", "\n", "Try to compute `g = 2*y0*T**(-2)`:\n", "multiplication of `int` (2) and\n", "`IntervalMath` (`y0`),\n", "and power operation `T**(-2)` are not defined" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class IntervalArithmetics:\n", " ...\n", " def __rmul__(self, other):\n", " if isinstance(other, (int, float)):\n", " other = IntervalMath(other, other)\n", " return other*self\n", "\n", " def __pow__(self, exponent):\n", " if isinstance(exponent, int):\n", " p = 1\n", " if exponent > 0:\n", " for i in range(exponent):\n", " p = p*self\n", " elif exponent < 0:\n", " for i in range(-exponent):\n", " p = p*self\n", " p = 1/p\n", " else: # exponent == 0\n", " p = IntervalMath(1, 1)\n", " return p\n", " else:\n", " raise TypeError('exponent must int')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding more functionality to the class: rounding\n", "\n", "\"Rounding\" to the midpoint value:" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = IntervalMath(5,7)\n", "float(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is achieved by" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class IntervalArithmetics:\n", " ...\n", " def __float__(self):\n", " return 0.5*(self.lo + self.up)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding more functionality to the class: repr and str methods" ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class IntervalArithmetics:\n", " ...\n", " def __str__(self):\n", " return '[%g, %g]' % (self.lo, self.up)\n", "\n", " def __repr__(self):\n", " return '%s(%g, %g)' % \\\n", " (self.__class__.__name__, self.lo, self.up)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstrating the class: $g=2y_0T^{-2}$" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "collapsed": false }, "outputs": [], "source": [ "g = 9.81\n", "y_0 = I(0.99, 1.01)\n", "Tm = 0.45 # mean T\n", "T = I(Tm*0.95, Tm*1.05) # 10% uncertainty\n", "print T" ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "collapsed": false }, "outputs": [], "source": [ "g = 2*y_0*T**(-2)\n", "g" ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# computing with mean values:\n", "T = float(T)\n", "y = 1\n", "g = 2*y_0*T**(-2)\n", "print '%.2f' % g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstrating the class: volume of a sphere" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "collapsed": false }, "outputs": [], "source": [ "R = I(6*0.9, 6*1.1) # 20 % error\n", "V = (4./3)*pi*R**3\n", "V" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print V" ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print float(V)" ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# compute with mean values:\n", "R = float(R)\n", "V = (4./3)*pi*R**3\n", "print V" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "20% uncertainty in $R$ gives almost 60% uncertainty in $V$" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 0 }