{ "metadata": { "name": "", "signature": "sha256:02a535e81b46e9c03a4d67807c160f8502e2bd334f91df67435ce73bc6f3d127" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# An Introduction to Objects & Object-Oriented Programming in Python (& Sundry Advanced Topics)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Programming Paradigms\n", "\n", "As it goes for most scientific methods, programming also has different paradigms associated with it, based on the thought process of the programmer. \n", "\n", "![Programming Paradigms](http://www.willamette.edu/~fruehr/haskell/pics/Paradigms.png)\n", "\n", "Today's lesson will walk you through some of these paradigms, focusing on Object Oriented Programming for the better part.\n", "## Contents\n", "- [Programming Paradigms](#intro)\n", "- [Procedural Programming](#mpl)\n", " - [Review of Functions](#review)\n", " - [Excerise 1](#Ex1)\n", "- [Object Oriented Programming](#oop)\n", " - [Motivation](#motiv)\n", " - [String objects](#sobjects)\n", " - [Exercise 2](#Ex2)\n", " - [Dictionaries and Lists](#DL)\n", " - [Iterables](#iter)\n", " - [List Comprhensions](#lc)\n", " - [Nested List Comprehensions](#nlc)\n", " - [Dictionaries](#dict)\n", " - [Namespaces](#ns) \n", " - [Your own objects](#own)\n", " - [Exercise 3](#Ex3)\n", "- [Objects, Part II](#oop2)\n", " - [Private Variables](#priv)\n", " - [Destructor](#dest)\n", " - [Inheritance](#inherit)\n", " - [Exercise 4](#Ex4)\n", "- [Functional Programming](#func)\n", "- [pip](#pip)\n", "- [Numerical example](#num)\n", "- [References](#refs)\n", "- [Credits](#credits)\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from __future__ import division\n", "\n", "import numpy as np\n", "import scipy as sp\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "\n", "#IPython magic command for inline plotting\n", "%matplotlib inline\n", "#a better plot shape for IPython\n", "mpl.rcParams['figure.figsize']=[15,3]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 16 }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Procedural Programming\n", "**Imperative programming** is a programming paradigm that describes computation in terms of statements that change a program state. In imperative programming, you tell the computer what to do. \"Computer, add x and y,\" or \"Computer, slap a dialog box onto the screen.\" The focus is on what steps the computer should take rather than what the computer will do (ex. C, C++, Java).\n", "\n", "**Structured programming** is any programming when functionality is divided into units like for loop, while loop, if... then etc block structure.\n", "\n", "**Procedural programming** is a programming paradigm, derived from structured programming, based upon the concept of the procedure call. Procedures, also known as routines, subroutines, methods, or functions, contain a series of computational steps to be carried out. Any given procedure might be called at any point during a program's execution.\n", "\n", "![Procedural Programming](http://i.stack.imgur.com/Ip5eR.jpg)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Review of functions\n", "\n", "The structure of a function is\n", " #def function_name(input arguments):\n", " #function defintion\n", " #return output\n", " \n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "#Example to find the area of a square\n", "def areaSquare(a):\n", " return a ** 2\n", "\n", "print areaSquare(3)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "9\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Exercise 1\n", "- Write a function, `sumArray`, which takes an array of numbers as the input and returns the sum of the array. (Use the NumPy documentation if you need to.)" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use this array to test the function you just wrote." ] }, { "cell_type": "code", "collapsed": false, "input": [ "x=np.linspace(1,10,10)\n", "print x\n", "print sumArray(x)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "---\n", "## Object Oriented Programming\n", "\n", "\n", "### Motivation for Object-Oriented Programming\n", "\n", "A programming _object_ is a data structure which seeks to model the real world by associating parameters and functions in a natural way. For instance, when you interact with your car, you don't think of an abstract button before changing your radio station, nor do you think of an abstract radio. You simply press the button on the radio. Similarly with code, it is often more natural to say `car.radio.freq.up()` than `increment(frequency(radio(car))))`, although both could be possible ways of thinking of the problem.\n", "\n", "_Object-oriented programming_ is thus a way of thinking about programs in terms of natural models. Each object possesses its own _member variables_ and _methods_, which are functions as a part of the object. An example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "#Pen.py \n", "class Pen(object):\n", " def __init__ (self):\n", " self.state = False\n", "\n", " def click(self):\n", " self.state = not self.state\n", "\n", " def write(self, in_str):\n", " if (self.state):\n", " print \"Writing message '%s'.\\n\"%in_str;\n", " else:\n", " print \"Oops! The pen is retracted.\\n\";\n" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This defines a possible object, or _class_, without actually creating one. Think of this as a Platonic archetype, if you're into that sort of thing; or else just as \"the idea of\" a pen, rather than an actual pen. To use an actual pen, we have to create it and then manipulate it:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "bic = Pen();\n", "\n", "bic.click();\n", "bic.write(\"hello world\");\n", "print bic.state" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Writing message 'hello world'.\n", "\n", "True\n" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "As another example, consider the following scenario.\n", "\n", "Bob and John are two students enrolled in CS 999\u2014User Interface Design. The professor for the course has given them a simple task as a part of developing an educational computer game for kids:\n", "\n", "- There will be three shapes displayed (triangle, square, and circle).\n", "- When the user clicks on one, it will display its area and circumference (perimeter). \n", "\n", "Both of them head to the library and start coding away. Here's what Bob-the-procedure-lover's code looks like:\n", "\n", " # Bob's code\n", " def area(shape):\n", " #Code to find the area for a shape\n", " \n", " def circum(shape):\n", " #Code to find the circumference of the shape\n", "\n", "Here is what John-the-OOP-guy wrote for his task:\n", "\n", " # John's code\n", " class Square:\n", " def area():\n", " #Code to find area of square\n", " def circum():\n", " #Code to find circumference of square\n", "\n", " class Circle:\n", " def area():\n", " #Code to find area of circle\n", " def circum():\n", " #Code to find circumference of circle\n", "\n", " class Triangle:\n", " def area():\n", " #Code to find area of triangle\n", " def circum():\n", " #Code to find circumference of triangle\n", "\n", "Both of them test their code extensively and hand it in.\n", "\n", "On the outside, it might seem like a more cumbersome task to define an **object** for each of the shapes and Bob, our procedural guy, seems to have the slick solution.\n", "\n", "But now (plot twist!), the professor pats both of them on the back and says, \"That was too easy. Now let's add an amoeba shape to the list of shapes and find its area and circumference too.\" Bob is slightly flustered but manages to alter his code by doing the following:\n", "\n", " # Bob's code\n", " def area(shape):\n", " if shape!=Amoeba:\n", " #Code to find the area for a shape\n", " else:\n", " #Code to find amoeba specific area\n", "\n", " def circum(shape):\n", " if shape!=Amoeba:\n", " #Code to find the circumference for a shape\n", " else:\n", " #Code to find amoeba specific circumference\n", "\n", "Bob seemed okay with his work but it still unnerved him because he had to modify his **previously tested code** and any spec change would force him to do so again. John is less worried because all he has to do is use another *class* to define an amoeba.\n", "\n", " # John's code\n", " class Amoeba:\n", " def area():\n", " #Code to find area of amoeba\n", " def circum():\n", " #Code to find circumference of amoeba\n", "\n", "But the the professor was not done (are they ever? :). He came up to them and said, \"Guys, sorry I forgot to mention this before, but I need you to approximate the area of your amoeba using the mid-point method.\"\n", "\n", "On hearing this Bob gets quite worked up and furiously modifies his code and yet again ends up having to change the twice-modified, twice-tested code.\n", "\n", " # Bob's code\n", " def area(shape,midpoint):\n", " if shape!=Amoeba:\n", " #Code to find the area for a shape\n", " else:\n", " #Use the midpoint of the amoeba to find the area\n", "\n", " def circum(shape):\n", " if shape!=Amoeba:\n", " #Code to find the circumference for a shape\n", " else:\n", " #Code to find amoeba specific circumference\n", "\n", "While for John it is a piece of cake, because he only has to modify the methods for the amoeba *class*.\n", "\n", " # John's code\n", " class Amoeba:\n", " def area(midpoint):\n", " #Code to find area of amoeba using midpoint\n", " def circum():\n", " #Code to find circumference of amoeba\n", "\n", "This brings us back around to the initial question\u2014why OOP? Object-oriented programming:\n", "\n", "- Makes your code more dynamic\n", "- Leaves your tested code untouched.\n", "- Has more cool features (and words) associated with it such as inheritance, abstraction, encapsulation etc. which we will go into.\n", "\n", "For now I hope we have convinced you sufficiently enough to take a trip with us to Objectville!\n", "\n", "![](http://www.safaribooksonline.com/library/view/head-first-javascript/9781449340124/httpatomoreillycomsourceoreillyimages1997343.png.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Strictly speaking, you've been working with objects in Python for a while even if you didn't realize it: lists, tuples, and dictionaries are all objects. Indeed, anything which uses a _method_, or member function, is an object in Python; thus `my_string.upper()` or even `'philadelphia'.capitalize()` are examples of string objects. Let's examine strings in more detail first." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "### String Objects\n", "\n", "As mentioned, strings are one of the most common examples of objects in Python, and they offer a number of illustrative and useful methods. Most of your classes will not need to be this versatile, but strings are still a prime example of how much to offer the user in terms of built-in functions." ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_string = 'sunday sunday sunday'\n", "my_string.upper()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 19, "text": [ "'SUNDAY SUNDAY SUNDAY'" ] } ], "prompt_number": 19 }, { "cell_type": "code", "collapsed": false, "input": [ "'philadelphia'.capitalize()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 20, "text": [ "'Philadelphia'" ] } ], "prompt_number": 20 }, { "cell_type": "code", "collapsed": false, "input": [ "# The following use of a member method `join` is astonishingly useful.\n", "elements = ['fire', 'earth', 'water', 'air']\n", "', '.join(elements)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 21, "text": [ "'fire, earth, water, air'" ] } ], "prompt_number": 21 }, { "cell_type": "code", "collapsed": false, "input": [ "# As is the use of `split`, particularly in data processing.\n", "pmu_data = '03-Apr-2014 00:55:07.500,60.0347,Good,0,Good,0,Good,121.275,Good,102.8688,Good,60.0353,Good,0,Good,0,Good,60.035,Good,0,Good,0,Good,123.285,Good,14.69637,Good,123.783,Good,50.32861,Good,0,Good,60.035,Good,0.01,Good,0,Good,112.985,Good,162.6308,Good,0.1530308,Good,132.8789,Good'\n", "pmu_data.split(',')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 22, "text": [ "['03-Apr-2014 00:55:07.500',\n", " '60.0347',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '121.275',\n", " 'Good',\n", " '102.8688',\n", " 'Good',\n", " '60.0353',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '60.035',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '123.285',\n", " 'Good',\n", " '14.69637',\n", " 'Good',\n", " '123.783',\n", " 'Good',\n", " '50.32861',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '60.035',\n", " 'Good',\n", " '0.01',\n", " 'Good',\n", " '0',\n", " 'Good',\n", " '112.985',\n", " 'Good',\n", " '162.6308',\n", " 'Good',\n", " '0.1530308',\n", " 'Good',\n", " '132.8789',\n", " 'Good']" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Unicode Strings**\n", "\n", "In Python 2, Unicode strings, or strings containing special characters beyond a certain limited set, must be explicitly marked as such. In Python 3, this restriction is gone and all strings are Unicode strings.\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "u'\u5df1\u6240\u4e0d\u6b32\uff0c\u52ff\u65bd\u65bc\u4eba\u3002 (\u5b54\u592b\u5b50)'\n", "#print u'\u5df1\u6240\u4e0d\u6b32\uff0c\u52ff\u65bd\u65bc\u4eba\u3002 (\u5b54\u592b\u5b50)'" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 23, "text": [ "u'\\u5df1\\u6240\\u4e0d\\u6b32\\uff0c\\u52ff\\u65bd\\u65bc\\u4eba\\u3002 (\\u5b54\\u592b\\u5b50)'" ] } ], "prompt_number": 23 }, { "cell_type": "code", "collapsed": false, "input": [ "u'S\u00e3o Paolo, Brasil'\n", "#print u'S\u00e3o Paolo, Brasil'" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 24, "text": [ "u'S\\xe3o Paolo, Brasil'" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Raw Strings**\n", "\n", "It is occasionally useful to write strings without escaping backslashes lying everywhere. Two cases that come to mind are hard-coded path names (`r'C:\\Python27\\Tools\\Scripts\\2to3.py'`) (incidentally normally a bad idea) and LaTeX strings (`r'\\frac{d}{dx}\\left(u(x)v(x)\\right)'`)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print \"\\tTab me over\"\n", "print r\"\\tDon't tab me over\"" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\tTab me over\n", "\\tDon't tab me over\n" ] } ], "prompt_number": 25 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Exercise 2\n", "\n", "- The following code loads a string of uppercase alphabet characters. Convert this string into a lowercase comma-delimited string in one line of code (_i.e._, `'a,b,c,...'`)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from string import ascii_uppercase\n", "ascii_uppercase" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Dictionaries & Lists as Objects\n", "\n", "Python by design provides a number of convenient functions for working with dictionaries and lists. We have worked with some of these methods in the past. \n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = [66.25, 333, 333, 1, 1234.5]\n", "a.insert(2, -1)\n", "a.append(333)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "### Iterables\n", "\n", "When it comes to dividing groups into pieces, many objects work \"naturally\" in Python. For instance:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "dogs = ['newfoundland', 'labrador', 'chow-chow', 'mutt']\n", "print 'I have %d dogs.'%len(dogs)\n", "for dog in dogs:\n", " print 'One kind of dog is a %s.'%dog" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "I have 4 dogs.\n", "One kind of dog is a newfoundland.\n", "One kind of dog is a labrador.\n", "One kind of dog is a chow-chow.\n", "One kind of dog is a mutt.\n" ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### List Comprehension\n", "\n", "List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.\n", "\n", "For example, assume we want to create a list of squares, like:\n", "\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "squares = []\n", "for x in range(10):\n", " squares.append(x**2)\n", "\n", "squares\n", "\n", "#Same as\n", "squares = [x**2 for x in range(10)]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Nested List Comprehensions\n", "\n", "The list comprehension operations can be exteneded to multi-dimensional lists too.\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ " matrix = [ [1, 2, 3, 4],\n", " [5, 6, 7, 8],\n", " [9, 10, 11, 12],\n", "]\n", " \n", "#List comprehension to evaluate transpose\n", "[[row[i] for row in matrix] for i in range(4)]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 29, "text": [ "[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]" ] } ], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Dictionaries\n", "\n", "Another useful data type built into Python is the dictionary. It is best to think of a dictionary as an unordered set of key: value pairs, with the requirement that the keys are unique (within one dictionary). A pair of braces creates an empty dictionary: {}. Placing a comma-separated list of *key:value* pairs within the braces adds initial key:value pairs to the dictionary; this is also the way dictionaries are written on output.\n", "\n", "The main operations on a dictionary are storing a value with some key and extracting the value given the key. \n", "\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "myfirstdict = {'a':1, 'b':2, 'c':3}\n", "\n", "myfirstdict['a']" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 11, "text": [ "1" ] } ], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Names, Namespaces, and Magic\n", "\n", "Occasionally you may have encountered odd phrases in Python, such as `if __name__ == \"__main__\":`. These are _reserved names_, which carry various special meanings to Python. (For instance, when you access a member variable in a function, you are (sometimes) actually invoking a function `__get_attr__` to access the variable.)\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "bic.__dict__" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 30, "text": [ "{'state': True}" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Incidentally, this is how you implement [_operator overloading_](https://docs.python.org/2/reference/datamodel.html#object.__lt__), but we'll leave this for now and touch on it again at the end." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Your Own Objects\n", "\n", "Now that we've seen what you can do with objects, let's design our own classes. A _class_ is a sort of template defining an _object_, which is _instantiated_ from a class and is a _class instance_. (I apologize for the terminology, as I had nothing to do with it's making. :)\n", "\n", "Objects are clearly models of the real world. Some examples include:\n", "\n", "- Student: name, age, sex, GPA, netid\n", "- Employee: department, salary, bonus\n", "- Mechanical objects: fans, tables, vehicles, just about anything\n", "\n", "![](http://upload.wikimedia.org/wikipedia/commons/thumb/9/98/CPT-OOP-objects_and_classes_-_attmeth.svg/300px-CPT-OOP-objects_and_classes_-_attmeth.svg.png)\n", "\n", "There are two important things to know about an **object**:\n", "\n", "- Things an object knows about itself are called **attributes**.\n", "- Things an object can do are called **methods**.\n", "\n", "So returning to the notion of a _class_: *a class is a blueprint for an object*. It tells the machine how to make an object of that particular type. Each object made from that class has its own values for the attributes of that class. (For example, you might use the Student class to make dozens of different students, and each student might have his own name, netID, GPA etc.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A Basic Object" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Dog:\n", " breed='abc'\n", " size=0\n", " name='xyz'" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 31 }, { "cell_type": "code", "collapsed": false, "input": [ "# Now let's create an object or an instance of the class Dog\n", "d=Dog()\n", "\n", "# The dot operator (.) gives you access to an object\u2019s state and behavior (instance variables and methods).\n", "d.name= 'Jack'\n", "print d.name" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Jack\n" ] } ], "prompt_number": 32 }, { "cell_type": "code", "collapsed": false, "input": [ "# Now let us add a method to the class\n", "class Dog:\n", " breed='abc'\n", " size=0\n", " name='xyz'\n", " \n", " def bark(self):\n", " print \"Ruff Ruff!\"" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 34 }, { "cell_type": "code", "collapsed": false, "input": [ "d=Dog()\n", "d.bark()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Ruff Ruff!\n" ] } ], "prompt_number": 35 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In practice, wherever you write `self` in your method, when you run the method that `self` is replaced by the name of the object, so when we call `d.bark()` the `self` is replaced by `d`. To understand this better, let us define another instance of `Dog` called `x`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x=Dog()\n", "x.bark()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Ruff Ruff!\n" ] } ], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Methods are similar to functions except that the first argument is usually `self` to reference any internal class attributes. We saw that we can alter the attribute values for an instance of a class by using the dot operator." ] }, { "cell_type": "code", "collapsed": false, "input": [ "x.name = 'Wolverine'\n", "x.breed = 'hound'\n", "size = 24 # inches tall at the shoulder" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rather than define every piece separately, it is more convenient to use the _constructor_." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Dog:\n", " ''' Attributes include breed, size and name'''\n", " \n", " # constructor\n", " def __init__(self, name='xyz', breed='abc', size=0):\n", " self.name= name\n", " self.breed= breed\n", " self.size= size\n", " \n", " def bark(self):\n", " print \"Ruff Ruff!\"" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 37 }, { "cell_type": "code", "collapsed": false, "input": [ "y=Dog('Jack','lab',10)\n", "print y.name\n", "print y.breed\n", "\n", "y=Dog()\n", "print y.name\n", "print y.breed" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Jack\n", "lab\n", "xyz\n", "abc\n" ] } ], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Exercise 3\n", "\n", "- Define a class `Canvas` with attributes `width` and `length`. Write two methods, `dims()` to display `width` and `length` of the canvas and `area()` to return the area of the canvas as `width*length`. Remember to include a constructor method to input the values of length and width." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's it for the basics. We'll address deeper questions like inheritance and private variables in Objects Part 2." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Objects, Part 2\n", "\n", "Now that you know a bit more about how objects go together and how several kinds of objects work in Python, let's take another look at the class definition itself.\n", "\n", "\n", "**Private Variables**\n", "\n", "In Python, you never declare variables, so there isn't an obvious way to hide them from outside users of your code (or make them _private_). If you haven't programmed with objects before, this may seem pointless, but there are actually good reasons for concealing internal state variables from users since it prevents them from mucking up your code, for instance. How do we actually make something private then?\n", "\n", "(_Encapsulation_, or data hiding, is the technical term for this: it hides variables except from certain related classes or from everything except the object itself. It's a good practice if you are concerned about concealing or glossing over complex behavior.) Consider the class `Account`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Account:\n", " def __init__(self, holder, number, balance, credit_line=1500): \n", " self.holder = holder \n", " self.number = number \n", " self.balance = balance\n", " self.credit_line = credit_line\n", " def deposit(self, amount): \n", " self.balance = amount\n", "\n", "acct = Account('Carrie Fisher', '492727ZED', 1300)\n", "print acct.holder, acct.number, acct.balance, acct.credit_line" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Carrie Fisher 492727ZED 1300 1500\n" ] } ], "prompt_number": 39 }, { "cell_type": "markdown", "metadata": {}, "source": [ "For good reasons, we would not want to be able to access all the details of this class directly from an instance or sometimes even from a *child* class. When we need to perform this kind of *data hiding*, we use the following type of attributes/methods. To hide these values from outside access, simply prefix them with the appropriate number of underscores `_`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
NameNotationScope
Public`name`visible everywhere
Protected`_name`accessible only by children and within object
Private`__name`accessible only within object
\n", "\n", "**Caveat programator**\n", "\n", "In languages like C++ or Java, we would be able to explicitly hide these values inside of the object such that they are completely inaccessible (_private_). However, in Python, the concept of private and protected member attributes/functions is purely notational (in order to understand which variables one should be wary of modifying). **Encapsulation in Python is more about packaging than about restriction.**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Account:\n", " def __init__(self, holder, number, balance, credit_line=1500): \n", " self._holder = holder \n", " self.number = number \n", " self.__balance = balance\n", " self.__credit_line = credit_line\n", " def deposit(self, amount): \n", " self.balance = amount\n", "\n", "class ChildAcc(Account):\n", " def getHolder(self):\n", " return self._holder # with two __ will fail, with one _ succeeds\n", "\n", "acct = Account('Carrie Fisher', '492727ZED', 1300)\n", "print acct.number\n", "\n", "#print acct.holder, acct.number, acct.balance, acct.credit_line # will fail\n", "\n", "chld = ChildAcc('Harrison Ford', 'C3POR2D2', 1250)\n", "print chld.getHolder()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "492727ZED\n", "Harrison Ford\n" ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\"Private\" methods and variables can be accessed through Python's \"name-mangling\" scheme: `_Class__member`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# How to access a \n", "class ChildAcc(Account):\n", " def __getHolder(self):\n", " return self._holder # with two __ will fail, with one _ succeeds\n", "\n", "chld = ChildAcc('Frank Oz', 'Y0D4', 900)\n", "print chld._ChildAcc__getHolder()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Frank Oz\n" ] } ], "prompt_number": 41 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "**Destructor**\n", "\n", "As we have a constructor method, there also exists a destructor method to delete the object and release the memory that it occupied as necessary." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Point:\n", " def __init( self, x=0, y=0):\n", " self.x = x\n", " self.y = y\n", " \n", " def __del__(self):\n", " class_name = self.__class__.__name__\n", " print class_name, \"deleted\"\n", "\n", "pt1 = Point()\n", "\n", "# The destructor `__del__` is called by `del`.\n", "del pt1" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Point deleted\n" ] } ], "prompt_number": 42 }, { "cell_type": "markdown", "metadata": {}, "source": [ "(Incidentally, the `del` keyword is useful for other manipulations: `del dictionary[\"alpha\"]` removes a key/value pair from a dictionary.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "**Inheritance**\n", "\n", "Consider the Canvas class that you created earlier. We now have a requirement to create a class `Rectangle` which represents a rectangular canvas. It would save us much time if we could just use the methods and attributes already used in the `Canvas` class. This is where OOP stands out\u2014by making reuse easy. This reuse is achieved by using **inheritance**, where a *child* class inherits from the *parent* class.\n", "\n", "The *child* class can access all of the *parent* class attributes and methods." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Rectangle(Canvas):\n", " def printRect(self):\n", " print \"Inside Rectangle Class\"\n", "\n", "r = Rectangle(10, 5)\n", "print r.area()\n", "\n", "r.printRect()" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'Canvas' is not defined", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mclass\u001b[0m \u001b[0mRectangle\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mCanvas\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mprintRect\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[1;34m\"Inside Rectangle Class\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[0mr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mRectangle\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m5\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mNameError\u001b[0m: name 'Canvas' is not defined" ] } ], "prompt_number": 43 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now this works for a rectangular `Canvas`. But if I needed a class with a `Square` canvas, then the calculation for the area would be different. OOP wins here as well, because it lets you *override* your parent method in the child class method." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class Square(Rectangle):\n", " def __init__(self,a):\n", " self.side=a\n", " def area(self):\n", " return self.side**2\n", "\n", "s = Square(4)\n", "print s.area()\n", "\n", "s.printRect()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Exercise 4\n", "\n", "- Create a class `Student` with the attributes\n", " - `name`\n", " - `uin`\n", " - `gender`\n", " - `major`\n", "\n", "Create a method to return the `major` of the student (apart from the constructor method).\n", "\n", "- Now create another class, `Graduate` which inherits from `Student` and add a new attribute `degree` to this class. Create a method in `Graduate` to return the `degree` of the student." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Test your code as follows.\n", "g = Graduate('John', '609248986', 'M', 'CS', 'PhD')\n", "g.getDegree()" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'Graduate' is not defined", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m# Test your code as follows.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mg\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mGraduate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'John'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'609248986'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'M'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'CS'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'PhD'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[0mg\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgetDegree\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mNameError\u001b[0m: name 'Graduate' is not defined" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Functional Programming\n", "\n", "A very different paradigm for thinking about programming from both procedural and object-oriented approaches is [_functional_ programming](https://en.wikipedia.org/wiki/Functional_programming). Essentially functional programming manipulates arrays by passing functions rather than passing arrays directly. Let's take a look at the basic pieces supported in Python to give you an idea of how this works in practice.\n", "\n", "(In Python 3, `import functools` to access `reduce` and some other tools.)\n", "\n", "`lambda`\n", "\n", "`map`\n", "\n", "`reduce`\n", "\n", "`filter`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**map()**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "items = [1, 2, 3, 4, 5]\n", "squared = []\n", "for x in items:\n", "\tsquared.append(x ** 2)\n", "\n", "squared\n", "[1, 4, 9, 16, 25]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 45, "text": [ "[1, 4, 9, 16, 25]" ] } ], "prompt_number": 45 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can simplify the above code by using the **map()** function" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def sqr(x): return x ** 2\n", "list(map(sqr, items))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 46, "text": [ "[1, 4, 9, 16, 25]" ] } ], "prompt_number": 46 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**filter()**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`filter` extracts each element in the sequence for which the function returns True. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "list(range(-5,5))\n", "[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]\n", "list( filter((lambda x: x < 0), range(-5,5)))\n", "[-5, -4, -3, -2, -1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 47, "text": [ "[-5, -4, -3, -2, -1]" ] } ], "prompt_number": 47 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**lambda()**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python supports the creation of anonymous functions (i.e. functions that are not bound to a name) at runtime, using the lambda construct." ] }, { "cell_type": "code", "collapsed": false, "input": [ "#Sieve of Eratosthenes\n", "nums = range(2, 50) \n", "for i in range(2, 8): \n", " nums = filter(lambda x: x == i or x % i, nums)\n", "print nums" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**reduce()**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) " ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 48, "text": [ "15" ] } ], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "What it is actually calculating - **((((1+2)+3)+4)+5)**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Code Evaluation & Variable Creation at Run Time\n", "\n", "`eval`, `exec`, `**kwargs`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**eval()**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`eval` is a built-in function (not a statement), which evaluates an expression and returns the value that expression produces." ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = 5 # x <- 5\n", "#x = eval('%d + 6' % x) # x <- 11\n", "#x = eval('abs(%d)' % -100) # x <- 100\n", "#x = eval('x = 5') # INVALID; assignment is not an expression.\n", "#x = eval('if 1: x = 4') # INVALID; if is a statement, not an expression." ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 49 }, { "cell_type": "code", "collapsed": false, "input": [ "x" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 50, "text": [ "5" ] } ], "prompt_number": 50 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**exec()**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`exec` is a statement in Python 2.x, and a function in Python 3.x. It compiles and immediately evaluates a statement or set of statement contained in a string." ] }, { "cell_type": "code", "collapsed": false, "input": [ "exec 'print(5)' # prints 5.\n", "exec 'print(5)\\nprint(6)' # prints 5{newline}6.\n", "exec 'if True: print(6)' # prints 6.\n", "exec '5' # does nothing and returns nothing." ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "5\n", "5\n", "6\n", "6\n" ] } ], "prompt_number": 51 }, { "cell_type": "markdown", "metadata": {}, "source": [ "**\\*\\*kwargs**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\\*\\*kwargs allows you to pass keyworded variable length of arguments to a function." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def greet_me(**kwargs):\n", " if kwargs is not None:\n", " for key, value in kwargs.iteritems():\n", " print \"%s == %s\" %(key,value)\n", " \n", "greet_me(name=\"abcd\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## pip\n", "\n", "[**pip**](https://pip.pypa.io/en/latest/) is a tool for installing Python packages from the Python Package Index. PyPI is a repository for open-source third-party Python packages. \n", "\n", "pip supports installing from PyPI, version control, local projects, and directly from distribution files." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", " $ pip install SomePackage # latest version\n", " $ pip install SomePackage==1.0.4 # specific version\n", " $ pip install 'SomePackage>=1.0.4' # minimum version\n", " $ pip install X --target=/path/to/local/pkgs #Specifying installation path" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import site\n", "print(site.getsitepackages()) # Return the path of the user base directory\n", "print(site.getusersitepackages()) # Return the path of the user-specific site-packages directory, USER_SITE" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "['C:\\\\Users\\\\Lakshmi\\\\AppData\\\\Local\\\\Enthought\\\\Canopy\\\\User', 'C:\\\\Users\\\\Lakshmi\\\\AppData\\\\Local\\\\Enthought\\\\Canopy\\\\User\\\\lib\\\\site-packages', 'C:\\\\Users\\\\Lakshmi\\\\AppData\\\\Local\\\\Enthought\\\\Canopy\\\\App\\\\appdata', 'C:\\\\Users\\\\Lakshmi\\\\AppData\\\\Local\\\\Enthought\\\\Canopy\\\\App\\\\appdata\\\\lib\\\\site-packages', 'C:\\\\Users\\\\Lakshmi\\\\AppData\\\\Local\\\\Enthought\\\\Canopy\\\\App\\\\appdata\\\\canopy-1.4.1.1975.win-x86_64', 'C:\\\\Users\\\\Lakshmi\\\\AppData\\\\Local\\\\Enthought\\\\Canopy\\\\App\\\\appdata\\\\canopy-1.4.1.1975.win-x86_64\\\\lib\\\\site-packages']\n", "C:\\Users\\Lakshmi\\AppData\\Roaming\\Python\\Python27\\site-packages\n" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "One can also use a direct URL to the Python package .\n", "\n", " $pip install git+git://github.com/jradavenport/cubehelix.git" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Personal PyPI\n", "\n", "If you want to install packages from a source other than PyPI, (say, if your packages are proprietary), you can do it by hosting a simple http server, running from the directory which holds those packages which need to be installed.\n", "\n", "For example, if you want to install a package called MyPackage.tar.gz, and assuming this is your directory structure:\n", "\n", " archive \n", "\n", " MyPackage\n", " \n", " MyPackage.tar.gz\n", "\n", "Go to your command prompt and type:\n", "\n", " $ cd archive\n", " $ python -m SimpleHTTPServer 9000\n", " \n", "This runs a simple http server running on port 9000 and will list all packages (like MyPackage). Now you can install MyPackage using any Python package installer. Using Pip, you would do it like:\n", "\n", " $ pip install --extra-index-url=http://127.0.0.1:9000/ MyPackage\n", " \n", "Other alternatives for a server could be [pypiserver](https://pypi.python.org/pypi/pypiserver) or the [Amazon S3](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSGettingStartedGuide/AWSCredentials.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Numerical Example\n", "\n", "Here is an example of a BVP solver implemented by using the OOP paradigm. The algorithm has a $\\mathcal{O}(n) $ time complexity. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "from __future__ import division\n", "import numpy as np\n", "import numpy.linalg as la\n", "import scipy.special as sp\n", "\n", "\n", "class CompositeLegendreDiscretization:\n", " \"\"\"A discrete function space on a 1D domain consisting of multiple\n", " subintervals, each of which is discretized as a polynomial space of\n", " maximum degree *order*. (see constructor arguments)\n", "\n", " There are :attr:`nintervals` * :attr:`npoints` degrees of freedom\n", " representing a function on the domain. On each subinterval, the\n", " function is represented by its function values at Gauss-Legendre\n", " nodes (see :func:`scipy.speical.legendre`) mapped into that\n", " subinterval.\n", "\n", " .. note::\n", "\n", " While the external interface of this discretization\n", " is exclusively in terms of vectors, it may be practical\n", " to internally reshape (see :func:`numpy.reshape`) these\n", " vectors into 2D arrays of shape *(nintervals, npoints)*.\n", "\n", " The object has the following attributes:\n", "\n", " .. attribute:: intervals\n", "\n", " The *intervals* constructor argument.\n", "\n", " .. attribute:: nintervals\n", "\n", " Number of subintervals in the discretization.\n", "\n", " .. attribute:: npoints\n", "\n", " Number of points on each interval. Equals *order+1*.\n", "\n", " .. attributes:: nodes\n", "\n", " A vector of ``(nintervals*npoints)`` node locations, consisting of\n", " Gauss-Legendre nodes that are linearly (or, to be technically correct,\n", " affinely) mapped into each subinterval.\n", "\n", " \"\"\"\n", "\n", "\n", " def __init__(self, intervals, order):\n", " \"\"\"\n", " :arg intervals: determines the boundaries of the subintervals to\n", " be used. If this is ``[a,b,c]``, then there are two subintervals\n", " :math:`(a,b)` and :math:`(b,c)`.\n", " (and the overall domain is :math:`(a,c)`)\n", "\n", " :arg order: highest polynomial degree being used\n", " \"\"\"\n", " self.intervals=intervals\n", "\n", " self.nintervals=len(intervals)-1\n", "\n", " self.npoints=order + 1\n", "\n", " #Initializing shifted_nodes\n", " shifted_nodes=np.zeros(self.npoints*self.nintervals) \n", "\n", " #Calling the scipy function to obtain the unshifted nodes\n", " unshifted_nodes=sp.legendre(self.npoints).weights[:,0]\n", "\n", " #Initializing shifted weights\n", " shifted_weights=np.zeros(self.nintervals*self.npoints)\n", "\n", " #Calling the scipy function to obtain the unshifted weights\n", " unshifted_weights=sp.legendre(self.npoints).weights[:,1]\n", "\n", " #Linearly mapping the unshifted nodes and weights to get the shifted\n", " #nodes and weights\n", " for i in range(self.nintervals):\n", " shifted_nodes[i*self.npoints:(i+1)*self.npoints]=(self.intervals[i]+ self.intervals[i+1])/2 + (self.intervals[i+1]-self.intervals[i])*(unshifted_nodes[0:self.npoints])/2\n", " shifted_weights[i*self.npoints:(i+1)*self.npoints]=(self.intervals[i+1]-self.intervals[i])*(unshifted_weights[0:self.npoints])/2\n", " \n", " #Setting nodes and weights attributes\n", " self.nodes=np.reshape(shifted_nodes,(self.nintervals,self.npoints))\n", " self.weights=np.reshape(shifted_weights,(self.nintervals,self.npoints))\n", " \n", " #Obtaining Vandermonde and RHS matrices to get A\n", " def vandermonde_rhs(m,arr):\n", " X=np.zeros((m,m))\n", " RHS=np.zeros((m,m))\n", " for i in range(m):\n", " for j in range(m):\n", " X[i][j]=arr[i]**j\n", " RHS[i][j]=((arr[i]**(j+1))-((-1)**(j+1)))/(j+1)\n", " return X,RHS\n", " \n", " A=np.zeros((self.npoints,self.npoints))\n", " X,RHS=vandermonde_rhs(self.npoints,unshifted_nodes)\n", "\n", " #Solving for spectral integration matrix\n", " A=np.dot(RHS,la.inv(X))\n", " \n", " self.A=A\n", "\n", " \n", "\n", " def integral(self, f):\n", " r\"\"\"Use Gauss-Legendre quadrature on each subinterval to approximate\n", " and return the value of\n", "\n", " .. math::\n", "\n", " \\int_a^b f(x) dx\n", "\n", " where :math:`a` and :math:`b` are the left-hand and right-hand edges of\n", " the computational domain.\n", "\n", " :arg f: the function to be integrated, given as function values\n", " at :attr:`nodes`\n", " \"\"\"\n", " int_val=0\n", " #Using Composite Gauss Quadrature to evaluate the whole integral\n", "\n", " for i in range(self.nintervals):\n", " f_val_arr=f[i,:]\n", " dot_val=np.dot(self.weights[i,:],f_val_arr.T)\n", " int_val+=dot_val\n", "\n", " return int_val\n", "\n", "\n", "\n", "\n", " def left_indefinite_integral(self, f):\n", " r\"\"\"Use a spectral integration matrix on each subinterval to\n", " approximate and return the value of\n", "\n", " .. math::\n", "\n", " g(x) = \\int_a^x f(x) dx\n", "\n", " at :attr:`nodes`, where :math:`a` is the left-hand edge of the\n", " computational domain.\n", "\n", " The computational cost of this routine is linear in\n", " the number of degrees of freedom.\n", "\n", " :arg f: the function to be integrated, given as function values\n", " at :attr:`nodes`\n", " \"\"\"\n", " #Initializing the left_indefinite_integral\n", " I=np.zeros((self.nintervals,self.npoints))\n", " current_quad_val=0\n", "\n", " for i in range(self.nintervals):\n", " \n", " if i>0:\n", " f_val_prev=f[i-1:i,:]\n", " weights_prev=self.weights[i-1:i,:]\n", " current_quad_val+=np.dot(weights_prev,f_val_prev.T)\n", " \n", " current_f_val=f[i,:]\n", "\n", " #Scaling the Spectral integration matrix by the interval length\n", " A_scale=np.dot((self.intervals[i+1]-self.intervals[i])/2,self.A)\n", "\n", " spectral_factor=np.dot(A_scale,current_f_val.T)\n", "\n", " #Adding the spectral factor to the evaluated definite integral\n", " indefinite_int=current_quad_val + spectral_factor\n", " I[i,:]=indefinite_int\n", " J=np.reshape(I,(self.nintervals*self.npoints,1))\n", " \n", "\n", " return I\n", "\n", " def right_indefinite_integral(self, f):\n", " r\"\"\"Use a spectral integration matrix on each subinterval to\n", " approximate and return the value of\n", "\n", " .. math::\n", "\n", " g(x) = \\int_x^b f(x) dx\n", "\n", " at :attr:`nodes`, where :math:`b` is the left-hand edge of the\n", " computational domain.\n", "\n", " The computational cost of this routine is linear in\n", " the number of degrees of freedom.\n", "\n", " :arg f: the function to be integrated, given as function values\n", " at :attr:`nodes`\n", " \"\"\"\n", " #Initializing the right integral\n", " I1=np.zeros(self.nintervals*self.npoints)\n", "\n", " #Unravelling the weights and function values at nodes\n", " f_right=f.ravel() \n", " weights_right=self.weights.ravel() \n", " \n", "\n", " for i in range(self.nintervals):\n", " \n", " #Evaluating Gauss Quadrature for all intervals from current to end\n", " f_next=f_right[i*self.npoints:self.npoints*self.nintervals]\n", " weights_next=weights_right[i*self.npoints:self.npoints*self.nintervals]\n", " current_quad_val=np.dot(weights_next,f_next.T)\n", " current_f_val=f_right[i*self.npoints:(i+1)*self.npoints]\n", " #Scaling the spectral integration matrix\n", " A_scale=np.dot((self.intervals[i+1]-self.intervals[i])/2,self.A)\n", " spectral_factor=np.dot(A_scale,current_f_val.T)\n", " \n", " #Subtracting spectral factor from Gauss Quadrature value\n", " I1[(i)*self.npoints:(i+1)*self.npoints]=np.dot(weights_next,f_next.T) - spectral_factor\n", " \n", " J=np.reshape(I1,(self.nintervals,self.npoints))\n", "\n", " \n", " return J\n", " \n" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 52 }, { "cell_type": "code", "collapsed": false, "input": [ "from __future__ import division\n", "#from legendre_discr import CompositeLegendreDiscretization\n", "import numpy as np\n", "import matplotlib.pyplot as pt\n", "\n", "\n", "def get_left_int_error(n, order):\n", " a = 2\n", " b = 30\n", " intervals = np.linspace(0, 1, n, endpoint=True) ** 2 * (b-a) + a\n", " discr = CompositeLegendreDiscretization(intervals, order)\n", "\n", " x = discr.nodes\n", " \n", " assert abs(discr.integral(1+0*x) - (b-a)) < 1e-13\n", "\n", "\n", " alpha = 4\n", " from scipy.special import jv, jvp\n", " f = jvp(alpha, x)\n", "\n", " num_int_f = jv(alpha, a) + discr.left_indefinite_integral(f)\n", " int_f = jv(alpha, x)\n", "\n", " if 0:\n", " pt.plot(x.ravel(), num_int_f.ravel())\n", " pt.plot(x.ravel(), int_f.ravel())\n", " pt.show()\n", "\n", " L2_err = np.sqrt(discr.integral((num_int_f - int_f)**2))\n", " return 1/n, L2_err\n", "\n", "\n", "def get_right_int_error(n, order):\n", " a = 2\n", " b = 30\n", " intervals = np.linspace(0, 1, n, endpoint=True) ** 2 * (b-a) + a\n", " discr = CompositeLegendreDiscretization(intervals, order)\n", "\n", " x = discr.nodes\n", "\n", " assert abs(discr.integral(1+0*x) - (b-a)) < 1e-13\n", "\n", " alpha = 4\n", " from scipy.special import jv, jvp\n", " f = jvp(alpha, x)\n", "\n", " num_int_f = jv(alpha, b) - discr.right_indefinite_integral(f)\n", " int_f = jv(alpha, x)\n", "\n", " if 0:\n", " pt.plot(x.ravel(), num_int_f.ravel())\n", " pt.plot(x.ravel(), int_f.ravel())\n", " pt.show()\n", " L2_err = np.sqrt(discr.integral((num_int_f - int_f)**2))\n", " return 1/n, L2_err\n", "\n", "\n", "def estimate_order(f, point_counts):\n", " n1, n2 = point_counts\n", " h1, err1 = f(n1)\n", " h2, err2 = f(n2)\n", "\n", " print \"h=%g err=%g\" % (h1, err1)\n", " print \"h=%g err=%g\" % (h2, err2)\n", "\n", " from math import log\n", " est_order = (log(err2/err1) / log(h2/h1))\n", " print \"%s: EOC: %g\" % (f.__name__, est_order)\n", " print\n", "\n", " return est_order\n", "\n", "\n", "if __name__ == \"__main__\":\n", " for order in [2, 3, 5, 7]:\n", " print \"---------------------------------\"\n", " print \"ORDER\", order\n", " print \"---------------------------------\"\n", " assert (estimate_order(lambda n: get_left_int_error(n, order), [10, 30])\n", " >= order-0.5)\n", " assert (estimate_order(lambda n: get_right_int_error(n, order), [10, 30])\n", " >= order-0.5)\n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "---------------------------------\n", "ORDER 2\n", "---------------------------------\n", "h=0.1 err=0.118027" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "h=0.0333333 err=0.00168249\n", ": EOC: 3.8691\n", "\n", "h=0.1 err=0.141862\n", "h=0.0333333 err=0.0016825\n", ": EOC: 4.03652\n", "\n", "---------------------------------\n", "ORDER 3\n", "---------------------------------\n", "h=0.1 err=0.0455367\n", "h=0.0333333 err=0.000125478\n", ": EOC: 5.36508\n", "\n", "h=0.1 err=0.0456416\n", "h=0.0333333 err=0.000125478\n", ": EOC: 5.36718\n", "\n", "---------------------------------\n", "ORDER 5\n", "---------------------------------\n", "h=0.1 err=0.00221849\n", "h=0.0333333 err=5.46588e-07\n", ": EOC: 7.56285\n", "\n", "h=0.1 err=0.00221849\n", "h=0.0333333 err=5.46588e-07\n", ": EOC: 7.56285\n", "\n", "---------------------------------\n", "ORDER 7\n", "---------------------------------\n", "h=0.1 err=6.48553e-05" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "h=0.0333333 err=1.47309e-09\n", ": EOC: 9.73278\n", "\n", "h=0.1 err=6.48553e-05\n", "h=0.0333333 err=1.47309e-09\n", ": EOC: 9.73278\n", "\n" ] } ], "prompt_number": 53 }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## References\n", "\n", "- [OOP in Python](http://www.tutorialspoint.com/python/python_classes_objects.htm)\n", "- [Classes](https://docs.python.org/2/tutorial/classes.html)\n", "- [Data Structures](https://docs.python.org/2/tutorial/datastructures.html)\n", "- [pip](https://pip.pypa.io/en/latest/user_guide.html#installing-packages)\n", "- [Head First Java](http://www.amazon.com/Head-First-Java-2nd-Edition/dp/0596009208)\n", "- [Programming Paradigms](http://www.willamette.edu/~fruehr/haskell/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "\n", "## Credits\n", "\n", "Neal Davis and Lakshmi Rao developed these materials for [Computational Science and Engineering](http://cse.illinois.edu/) at the University of Illinois at Urbana\u2013Champaign.\n", "\n", "\n", "This content is available under a [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/).\n", "\n", "[![](https://bytebucket.org/davis68/resources/raw/f7c98d2b95e961fae257707e22a58fa1a2c36bec/logos/baseline_cse_wdmk.png?token=be4cc41d4b2afe594f5b1570a3c5aad96a65f0d6)](http://cse.illinois.edu/)" ] } ], "metadata": {} } ] }