{ "metadata": { "name": "", "signature": "sha256:31f4e126940681137b4a1839647ca564ca2abb3f10346f27827b879efef33677" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Python's Class Development Toolkit" ] }, { "cell_type": "code", "collapsed": true, "input": [ "\"\"\"Circuminferential, LLC\n", " An Advanced Circle Analytics Company\n", "\"\"\"\n", "\n", "import math\n", "\n", "class Circle(object):\n", " \"An advanced circle analytic toolkit\"\n", " \n", " version = '0.1' # class variable\n", " \n", " def __init__(self, radius):\n", " self.radius = radius\n", " \n", " def area(self):\n", " \"Perform quadrature on myself.\"\n", " return math.pi * (self.radius ** 2.0)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": true, "input": [ "# Tutorial\n", "\n", "print 'Circuminferential Circle, version', Circle.version\n", "print\n", "c = Circle(10)\n", "print 'A circle of radius', c.radius\n", "print 'has an area of', c.area()\n", "print" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": true, "input": [ "help(Circle)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": true, "input": [ "c.__dict__" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": true, "input": [ "# woo, application by first customer (Academia)!\n", "\n", "from random import random, seed\n", "\n", "seed(8675309)\n", "print 'Using Circuminferential(tm) Circle, version', Circle.version\n", "print\n", "n = 10\n", "circles = [Circle(random()) for i in xrange(n)]\n", "print 'The average area of', n, 'random circles'\n", "avg = sum(c.area() for c in circles) / n\n", "print 'is %.1f' % avg\n", "print" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# New version: perimeter method added\n", "\n", "import math\n", "\n", "class Circle(object):\n", " \"An advanced circle analytic toolkit\"\n", " \n", " version = '0.2' # API change -> new version!\n", " \n", " def __init__(self, radius):\n", " self.radius = radius\n", " \n", " def area(self):\n", " \"Perform quadrature on myself.\"\n", " return math.pi * (self.radius ** 2.0)\n", " \n", " def perimeter(self):\n", " \"Find my perimeter.\"\n", " return 2.0 * math.pi * self.radius\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "help(Circle)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# the rubber sheet company's code\n", "\n", "cuts = [0.1, 0.7, 0.8]\n", "circles = [Circle(r) for r in cuts]\n", "for c in circles:\n", " print 'a circlet with a radius of', c.radius\n", " print 'has a perimeter of', c.perimeter()\n", " print 'a cold area of', c.area()\n", " c.radius *= 1.1\n", " print 'and a warm area of', c.area()\n", " print" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# the tire company's code\n", "\n", "class Tire(Circle):\n", " \"Tire: a circle with a corrected perimeter\"\n", " \n", " def perimeter(self):\n", " \"Find my circumference (corrected for the rubber).\"\n", " return Circle.perimeter(self) * 1.25\n", " \n", "\n", "t = Tire(22)\n", "print \"A tire of radius\", t.radius\n", "print \"has an inner area of\", t.area()\n", "print \"and an odometer corrected perimeter of\", t.perimeter()\n", "print" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# the graphics company's code\n", "\n", "def bbd_to_radius(bbd):\n", " \"Find radius of a circle with bounding box diagonal `d`.\"\n", " return bbd / (2.0 * math.sqrt(2.0))\n", "\n", "bbd = 25.1\n", "c = Circle(bbd_to_radius(bbd))\n", "print \"A circle with a bbd of 25.1\"\n", "print \"has a radius of\", c.radius\n", "print \"and an area of\", c.area()\n", "print" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# alternative constructors\n", "\n", "from datetime import datetime\n", "\n", "print datetime(2013, 3, 16)\n", "print datetime.fromtimestamp(1363383616)\n", "print datetime.fromordinal(734000)\n", "print datetime.now()\n", "print\n", "print dict.fromkeys(['spam', 'eggs', 'shrubbery'])" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Another new version: alternative constructor -> new API -> version bump\n", "\n", "import math\n", "\n", "class Circle(object):\n", " \"An advanced circle analytic toolkit\"\n", " \n", " version = '0.3'\n", " \n", " def __init__(self, radius):\n", " self.radius = radius\n", " \n", " def area(self):\n", " \"Perform quadrature on myself.\"\n", " return math.pi * (self.radius ** 2.0)\n", " \n", " def perimeter(self):\n", " \"Find my perimeter.\"\n", " return 2.0 * math.pi * self.radius\n", "\n", " @classmethod # class method decorator\n", " def from_bbd(cls, bbd): # alternative constructor\n", " \"Construct a circle using a bounding box diagonal.\"\n", " radius = bbd / (2.0 * math.sqrt(2.0))\n", " return cls(radius)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Let's try out the alternative constructor\n", "c = Circle.from_bbd(25.1)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Cool. So what radius do we get?\n", "c.radius" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Yet another new version, adding the 'diameter' property\n", "\n", "import math\n", "\n", "class Circle(object):\n", " \"An advanced circle analytic toolkit\"\n", " \n", " version = '0.4' # another API change\n", " \n", " def __init__(self, radius):\n", " self.radius = radius\n", " \n", " def area(self):\n", " \"Perform quadrature on myself.\"\n", " return math.pi * (self.radius ** 2.0)\n", " \n", " @property # property decorator\n", " def diameter(self): # diameter \"computed attribute\"\n", " \"Get my diameter (i.e., compute from radius).\"\n", " return self.radius * 2.0\n", " \n", " @diameter.setter # this makes it so we can set the diameter\n", " def diameter(self, diameter): # and the new radius is automatically computed and set\n", " \"Set my diameter (and computed radius).\"\n", " self.radius = diameter / 2.0\n", " \n", " def perimeter(self):\n", " \"Find my perimeter.\"\n", " return 2.0 * math.pi * self.radius\n", "\n", " @classmethod # class method decorator\n", " def from_bbd(cls, bbd): # alternative constructor\n", " \"Construct a circle using a bounding box diagonal.\"\n", " radius = bbd / (2.0 * math.sqrt(2.0))\n", " return cls(radius)\n", " \n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Let's try out the diameter property ...\n", "c = Circle(10)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# check radius and diameter\n", "c.radius" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "c.diameter" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# change diameter to see what happens ...\n", "c.diameter *= 1.1" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "c.diameter" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# show that radius tracks diameter (because of diameter setter fn)\n", "c.radius" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# check the instance dictionary (before adding __slots__ to the class, in the next block of code)\n", "c.__dict__" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Now the slots version ...\n", "\n", "import math\n", "\n", "class Circle(object):\n", " \"An advanced circle analytic toolkit\"\n", " \n", " version = '0.5' # another API change\n", " \n", " __slots__ = ['radius']\n", " \n", " def __init__(self, radius):\n", " self.radius = radius\n", " \n", " def area(self):\n", " \"Perform quadrature on myself.\"\n", " return math.pi * (self.radius ** 2.0)\n", " \n", " @property # property decorator\n", " def diameter(self): # diameter \"computed attribute\"\n", " \"Get my diameter (i.e., compute from radius).\"\n", " return self.radius * 2.0\n", " \n", " @diameter.setter # this makes it so we can set the diameter\n", " def diameter(self, diameter): # and the new radius is automatically computed and set\n", " \"Set my diameter (and computed radius).\"\n", " self.radius = diameter / 2.0\n", " \n", " def perimeter(self):\n", " \"Find my perimeter.\"\n", " return 2.0 * math.pi * self.radius\n", "\n", " @classmethod # class method decorator\n", " def from_bbd(cls, bbd): # alternative constructor\n", " \"Construct a circle using a bounding box diagonal.\"\n", " radius = bbd / (2.0 * math.sqrt(2.0))\n", " return cls(radius)\n", " " ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# those Academia guys, always pushing the limits ...\n", "\n", "from random import random, seed\n", "\n", "n = 10000000 # yikes!\n", "\n", "seed(8675309)\n", "print 'Using Circuminferential(tm) Circle, version', Circle.version\n", "circles = [Circle(random()) for i in xrange(n)]\n", "print 'The average area of', n, 'random circles'\n", "avg = sum(c.area() for c in circles) / n\n", "print 'is %.1f' % avg\n", "print\n", "# this may take several seconds ...\n", "# (slots improve memory footprint, but not necessarily performance)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# we expect an exception in this block -- no instance dict now!\n", "c = Circle(9)\n", "c.__dict__" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# a final look at our internal docs\n", "help(Circle)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Exercises" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* add a 'bbd' property to the **Circle** class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* the graphics company uses several shape classes, besides our Circle -- try creating an Ellipse, or maybe even a Sphere ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* try applying slots to the OOPI N-body code's **Particle** class" ] } ], "metadata": {} } ] }