CS30 - Spring 2016 - Class 15

Example code in this lecture


Lecture notes

  • administrative
       - assignment 5 back soon
       - assignment 6 due tomorrow
          - be clear and concise
          - analyze three things:
             - performance (both training error and testing error, where appropriate)
             - reliability: how often does it achieve that performance
             - convergence: how quickly does it get there
          - nested for loop

  • objects
       - objects have two pieces of information:
          - data
          - methods
       - for example, a list is an object
          >>> l = [1, 2, 3, 4]
          >>> l.append(5)
          >>> l
          [1, 2, 3, 4, 5]
          >>> l.reverse()
          >>> l
          [5, 4, 3, 2, 1]

  • method types
       - there are two types of methods:
          - mutator methods
             - change or mutate the object
          - accessor methods
             - do not change the object
             - ask questions about the object
       - you can look at all of the methods available for an object using help:
          >>> help(l)

          >>> help(list) # the type of the object

  • constructors
       - every object has a special method called a constructor
       - it has the same name as the type of the object and can be called on it's own to "construct" a new object
       - Many of the methods we've actually been using already are actually constructors!
          >>> str(10)
          >>> int("10")
          >>> float(10)

          - all of these construct a new object of that type
       - Some things (like lists and tuples) have special ways of constructing them, but they too also have constructors
          >>> list()
          >>> tuple()
          >>> list( "abcde" )
          ['a', 'b', 'c', 'd', 'e']
          >>> tuple( "abcde" )
          ('a', 'b', 'c', 'd', 'e')
          >>> tuple( [1, 2, 3, 4] )
          (1, 2, 3, 4)

  • classes
       - a "class" is the blueprint describing what data and methods an object will have
       - an object is an instance of a class
          - for example, we could define a class people
             - people have attributes
             - people will have methods
             - when we define a particular person, it is an object that is an instance of the class or people
       - classes define types
          - in Python, since all things are objects, then they all represent instances of objects
          - though in other languages, you could have a type that is not defined by a class

  • defining our own classes
       - syntax:

          class name_of_class:      
             # methods in the class

       - look at Person class in person.py code
          - 5 methods
          - 2 "special" methods
             - methods that are surrounded by two underscores on each side are "special" methods
             - they are generally NOT called directly
             - __init__ defines the constructor for the method
             - __str__ defines what the object will be when it's printed

       - self
          - self is a variable that allows us to store data and retrieve data in an object
          - self is a reference to the current object
          - it should be the first parameter to every method in a class (not totally true, but fine for this class)
             - though you do NOT include it when you call the methods (this is a little annoying, but you'll get used to it)

       - look at the constructor (__init__ method) in person.py code
          - takes self and two parameters
          - all it does is SAVE these parameters into the object

       - look at the "get_" methods in person.py code
          - these are accessor methods
          - give us information back
          - notice they only take self as a parameter and use that to give back characteristics about the object

       - look at the "change_shirt" method

  • write a class called Rectangle
       - that has a constructor that takes x1, y1, x2, y2 (and self)
       - and one method called area that gives back the area of the rectangle
  • look at the Rectangle class in rectangle.py code
       - many different ways we could have implemented this
       - in this version, I chose to store the two points as x, y pairs, specifically 4 instance variables:
       - init
          - just saves away the input parameters into instance variables so we can use them later
             self.x1 = x1

             says take the parameter x1 and put its value into the instance variable self.x1
          - this creates 4 instance variables
       - area
          - uses the four instance variables to calculate the width and height of the rectangle
          - we need to use abs since we don't specify that the x,y pairs are lower left and upper right corners

  • why classes?
       - encapsulation
          - look at the Rectangle class in rectangle2.py code
             - we have the exact same set of methods (constructor and area)
             - however, we have *different* internal representation
                - we store the lower left hand corner (x,y)
                - and the width and height of the rectangle
             - notice that we can ask the exact same questions using this representation
          - anyone using the Rectangle class should NOT care which version we use
             - both have the same set of methods and the same functionality
             - this is the power of using classes, a general framework called object-oriented programming

       - modularity
          - functions create single units that we can use to build up other functions
          - in the same way, classes allow us to create functional units (in this cases a class of objects with a particular behavior)

       - avoid naming conflicts

  • look at the Queue class in queue.py code
       - has 5 methods (constructor, str, and three methods we wrote
       - what data does it keep, i.e. what are the instance variables?
          - just self.queue, which is a list
       - constructor
          - the constructor has an optional parameter and can be called with either zero parameters or with a list:
             >>> q = Queue()
             >>> print q
             The queue contains: []
             >>> q = Queue([1, 2, 3, 4])
             >>> print q
             The queue contains: [1, 2, 3, 4]

          - if it's given a list as a parameter it *copies* it using slicing (:) and saves that away in the instance variable
             - why copy it?
                - to avoid aliasing!
                - otherwise, the instance variables (self.queue) would reference the same list as was passed in (a bad thing!)

       - look at enqueue and dequeue. What is this class?
          - way of storing data
             - implemented like a line
             - first things to be enqueued (added) are the first things to be dequeued (removed)
             - sometimes called FIFO (first in first out)
          - enqueue adds elements to the *back* of the list
          - dequeue removes elements from the *front* of the list

       - Example usage:
          >>> q = Queue()
          >>> q.add(1)
          >>> q.add(10)
          >>> q.add(2)
          >>> print q
          The queue contains: [1, 10, 2]
          >>> q.remove()
          >>> q.remove()
          >>> q.remove()
       - is_empty
          - just checks if the queue has anything in it

       - notice that underneath the covers, this is still just a list
       - by hiding the list in the class, we have:
          - provided a clear small set of methods that defines how we can interact with the object (the queue)
          - hid the implementation details from whoever uses it
             - we used a list, but could have used something else
             - in a similar way, we could have added to the front of the list and removed from the back and still achieved exactly the same functionality

  • look at Stack class in stack.py code
       - what does it do?
          - same basic interface as the Queue class
             - constructor and str methods
             - is_empty
             - a method to add things (push)
             - a method to remove things (pop)
       - the difference is that stacks add and remove things from the *same* end of the list
          - think of it like a stack of plates
             - the last plate that you put on top
             - will be the first one to be removed
             - sometimes called LIFO (last in first out)

       - For example, if we add the same elements as we did before, we get them in the opposite order
          >>> s = Stack()
          >>> s.add(1)
          >>> s.add(10)
          >>> s.add(2)
          >>> s.remove()
          >>> s.remove()
          >>> s.remove()