Python Classes

Everything is an object

Thus far in Python we've been working with strings, numbers, lists, dictionaries, tuples... These are all types of objects in Python. Everything is an object, and every object has a type. Let's verify this:

>>> s = 'some string'
>>> type(s)
<type 'str'>

>>> str
<type 'str'>

>>> type(s) is str
True

Types and instances

So s we call an instance of the str type. Let's look at an instance of the list and bool types:

>>> mylist = [1,2,3]
>>> type(mylist)
<type 'list'>

>>> myboolean = False
>>> type(myboolean)
<type 'bool'>

We must all learn to think as individuals

Why does it matter what type an object (or instance) is? For one thing, it determines what methods you can call on it. Last week we saw that if you have a string instance s, you can call s.replace('a', 'b') to replace all 'a's in the string with 'b's. But if you have a list mylist and you try to call mylist.replace(...), you'll get an error. The list type has no replace method, so list instances don't either.

>>> mylist.replace('a', 'b')
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'list' object has no attribute 'replace'

User-defined types

str, list, dict, bool, and tuple are all built-in types. But we can also define our own user-defined types, which are called classes. Most of you have probably seen classes before, in Java or PHP or Perl (sort of).

A sample class

Let's say we're writing a blog engine. Clearly we'll need to work with blog posts. So we'll want a class to represent a blog post:

class BlogPost(object):
    """ 
    A blog post with a title and content.

    """
    def __init__(self, title, content=''):
        self.title = title
        self.content = content

The __init__ method is "magic": it's called whenever a new instance of your class is created. It's usually used for initialization, as we do here. (A "method" is just what we call a function when it belongs to a class).

Bring out your posts

>>> my_post = BlogPost('The Title')

>>> my_post.title
'The Title'

>>> my_post.content
''

>>> my_post.content = "Some really long content.\n\nAnother paragraph."

>>> print my_post.content
Some really long content.

Another paragraph.

That bird wouldn't FOOM if you put a million volts through it

So far our BlogPost objects are kind of... dead. They don't do anything. They can hold data (a title and some content), but they have no methods (besides __init__). Let's add one:

class BlogPost(object):
    def __init__(self, title, content=''):
        self.title = title
        self.content = content

    def get_first_paragraph(self):
        """
        Return the first paragraph of this blog post's content.

        """
        return self.content.split('\n\n')[0]

Trying out get_first_paragraph()

>>> my_post = BlogPost('The Title', "Some content.\n\n"
...                                 "Another paragraph\n\n"
...                                 "A third paragraph.")

>>> print my_post.content
Some content.

Another paragraph.

A third paragraph.

>>> print my_post.get_first_paragraph()
Some content.

Class inheritance

Our BlogPost class definition begins with the line:

class BlogPost(object):

Our class inherits from the object class. This means that, before we've written any further code, it already has any methods defined on that class. In this case, object is a generic base class, a blank slate. Any class you write which doesn't inherit from some other class should inherit from object. (You may see this referred to as a "new-style" class; "old-style classes" inherit from nothing at all, and are only for backwards-compatibility, they shouldn't be used in new code).

Why use inheritance?

Use inheritance to create more specialized types of general classes. For instance, some blog posts are really nothing but a link to some external URL with a bit of commentary. It may be useful to have a specialized LinkPost class for those posts. LinkPosts are still BlogPosts too; if we define new methods (or change existing methods) on BlogPost, we want LinkPost to get those changes automatically (we don't want to duplicate code and have to change the same code two different places).

LinkPost

class LinkPost(BlogPost):
    " A subclass of BlogPost for external links. "
    def __init__(self, title, link, content=''):
        super(LinkPost, self).__init__(title, content)
        self.link = link

    def get_first_paragraph(self):
        first_para = super(LinkPost, self).get_first_paragraph()
        return "\n".join([first_para, self.link])

Using LinkPost

>>> my_link = LinkPost('Check this out', 
...                    'http://www.example.com',
...                    'What a beautiful example URL.')

>>> print my_link.get_first_paragraph()
What a beautiful example URL.
http://www.example.com

Homework