Programming 1
Week 8: Notes

Some of this week's topics are covered in Introducing Python:

Here are some additional notes.

class objects

In Python, every class is itself an object. For example, let's revisit the Point class from the last lecture:

class Point:
  def __init__(self, x, y):
    self.x = x
    self.y = y

>>> Point
<class '__main__.Point'>

This is how Python prints out a class. It is an object like any other. For example, we can put it into a variable:

>>> xyz = Point
>>> xyz
<class '__main__.Point'>

testing an object's type

Let's look at an instance of the Point class:

>>> p
<__main__.Point object at 0x7fc34f4107d0>
>>> type(p)
<class '__main__.Point'>



The type() function returns the type of any object. In an object-oriented language such as Python, all types are classes, so type() reveals an object's class. We can confirm that type(p) is the class object itself:

>>> type(p) is Point
True

More generally, we can use Python's isinstance() function to check whether an object is an instance of a class or any of its subclasses. For example, in the last lecture, we defined a class Queue and a subclass AvgQueue that extends Queue. Suppose that we create an instance of AvgQueue:

>>> q = AvgQueue()

Now q is an AvgQueue, and is also a Queue, because every AvgQueue is a Queue with extra attributes. We can verify this using isinstance():

>>> isinstance(q, AvgQueue)
True
>>> isinstance(q, Queue)
True

class attributes

Because a class is an object, we can assign attributes to it. For example:

>>> Point.abc = 7

>>> print(Point.abc + 1)

8

Class attributes are distinct from instance attributes. Each instance of the Point class has its own values of x and y, but there is only one value of the abc attribute, shared by all Point instances.

We might use a class attribute to store a constant instance of a class, for example:

>>> Point.origin = Point(0, 0)

As another example, suppose that we have a Student class, and each student has its own integer ID. We could use a class attribute to store the next ID to be assigned:

class Student:
  next_id = 0

  def __init__(self, name):
    self.name = name
    self.id = Student.next_id
    Student.next_id += 1

Notice that we can initialize a class attribute inside a class definition. Python will initialize this attribute only once, as it reads the class definition - not every time it creates a new instance of a class.

static methods

We may also define methods that belong to a class but do not run on any particular instance of the class. These are called static methods.

For example, in the Student class above we could add a method that adds 100 to the next_id counter. Perhaps we'd like to do this at the beginning of each new semester, so that students who begin in different semesters have ids that are not too close:

class Student:

  

  def new_semester():
    Student.next_id += 100

Notice that a static method does not take self as an argument, since it does not run on any instance of the class. We can invoke it through the class itself:

Student.new_semester();

Another common use of static methods is to provide various ways to construct an object. For example:

class Point:
  
  def random():    # construct a random Point
    return Point(randint(-100, 100), randint(-100, 100))

list comprehensions

See the Introducing Python book for an introduction to list comprehensions. Here are some useful examples:

Adding 1 to each element in a list:

m = [i + 1 for i in l]

Generating a list of all perfect squares from 1 to 400:

squares = [i * i for i in irange(1, 20)]

A solution to Project Euler's Problem 1 (find the sum of all the multiples of 3 or 5 below 1000):

sum([i for i in range(1000) if i % 3 == 0 or i % 5 == 0])

A function that builds a nested list representing a matrix of zeroes:

def empty(rows, cols):
    return [cols * [0] for r in range(rows)]

A function that builds a table that holds the value (2 * i + j) for all values 0 ≤ i, j ≤ 10:

def table():
    return [[2 * i + j for j in range(11)] for i in range(11)]

Finding all triples of integers (a, b, c) such that a2 + b2 = c2, with 0 ≤ a < b < c ≤ 20:

[(a, b, c) for c in range(21)
           for b in range(c)
           for a in range(b)
           if a * a + b * b == c * c]

set comprehensions

As an example of a set comprehension, we can construct the set of all numbers that are the product of two single-digit integers:

{ a * b for a in range(1, 10) for b in range(1, 10) }

dictionary comprehensions

As an example of a dictionary comprehension, here is a function that flips a dictionary, i.e. inverts the keys and values:

def flip(d):
    return { val : key for key, val in d.items() }