• Last week we looked at complex data structures in Python.
  • Complex data structures are useful but they have some drawbacks:
    • They're easy to mess up!
    • It's difficult to keep your data straight
  • The data structures we created are ad-hoc data structures because they have no formal definition

The problem with Ad Hoc data structures is that it's easy to mix stuff up:

blog1 = {
  'title' : 'First post!', 
  'text'  : 'This is my first post.',
blog2 = {
  'title' : 'Another day...', 
  'body'  : "I'm messing up ad-hoc data today.",
blogs = [ blog1, blog2 ]   

Notice the problem? The first blog post has the keys title and text. The second one has keys title and body. If you were to use this for loop to access blogs a KeyError will happen.

for blog in blogs : 
  print ('Title:', blog['title'])
  print (blog['text'])


  • A package is just some source code in a file.
  • The name of the package comes from the name of the file.
  • Packages are imported with the import keyword

We've done this before:

import sys 
print ('The program is:', sys.argv[0]) 
  • Packages are given a name in the local context
    • In the example the name is sys
    • You can control the name of the imported package

Here's another way:

import sys as foo
print ('The program is:', foo.argv[0]) 
  • Notice that you access the package via the name you give it
  • Access to package members is via the dot
  • You can see what functions and variables are in a package with the dir() function.
>>> import sys 
>>> for member in dir(sys) :
...   print (member) 
  • The dot is a vital piece of Python syntax.
  • We've used it a lot!

Consider the following:

>>> s = 'Mary had a little lamb' 
>>> l = s.split() 
>>> l.remove('lamb')
>>> l.append('emu') 
>>> ' '.join(l) 
'Mary had a little emu'
  • In the examples you use the dot to access functions that belong to strings and lists
  • Packages are the same

Consider the file:
'''A package for functions I like''' 
myvar = "This is a variable."
def myfunc() :
    '''This is my favorite function.''' 
    return "Mike"

Now use your functions:

>>> import functions as f 
>>> dir(f) 
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'myfunc', 'myvar']
>>> f.myvar 
'This is a variable.'
>>> f.myfunc() 
  • In many ways packages are like dictionaries
    • Classes too!
  • Instead of accessing them with [] you access them with the dot.
    • Names are restricted to valid names for python variables and functions.
    • But the dot is way less typing!

Classes and Objects

  • Python is an object oriented programming language.
  • Object oriented means that you can create your own data types in Python.
  • Data types have three essential ingredients:
    • A name
    • Class variables
    • Member functions

Let's start with a class that has a name and nothing else:

class Simple : 
  • A class is like a template for making something (like a cookie cutter).
  • You use the template to make an object (called an instance)
  • The distinction between the class and the instance is subtle at first.

Here's showing how to get an instance of an object:

>>> class Simple:
...   pass
>>> inst = Simple() 
>>> type(inst)
<class '__main__.Simple'>
>>> = 'The first instance' 
'The first instance'

You can create as many instances as you like and they are all different!

>>> inst2 = Simple()
>>> = 'The second instance' 
>>> inst3 = Simple()
>>> = 'foo not name is okay!' 
'The first instance'
'The second instance'
'foo not name is okay!'

Member Functions

  • Classes have member functions
  • Member functions are the best way to access class variables
  • Member functions receive the special self variable
class BlogEntry : 
    def setTitle(self, new_title) : 
        self.title = new_title    
b = BlogEntry()
b.setTitle('This is the new title') 
  • The self variable contains the instance of the class that's being accessed.
  • The picture shows the linked variables.

  • There is one important special member function named __init__.
  • The __init__ function is called every time a new instance of your class is made.
    • It's known as a constructor

Here's an example of a class with an __init__ function:

>>> class Init : 
...   def __init__(self) : 
...     print ('This is __init__(', self, ')')
>>> i = Init() 
This is __init__( <__main__.Init object at 0x7faba5c2d208> )
>>> i 
<__main__.Init object at 0x7faba5c2d208>

Notice how creating an instance of Init automatically calls the constructor.

  • It's customary to create any class variables that the class will use in init.
    • You should avoid creating them outside of the class!

Let's update our BlogEntry class with a constructor:

class BlogEntry : 
    def __init__(self) :
        self.title = '' 
        self.text = ''         
  • By assigning self.title and self.text in __init__ we guarantee that those values are present.
  • But, we can do better than that!
  • Constructors can take arguments.

Let's make the constructor better:

class BlogEntry : 
    def __init__(self, title, text) :
        self.title = title 
        self.text = text
  • When constructors take arguments you have to change how you create an instance.
  • The instance has to be created with the arguments.

Now we have to create a BlogEntry like this:

>>> class BlogEntry : 
...     def __init__(self, title, text) :
...         self.title = title 
...         self.text = text
>>> ent = BlogEntry('Blog Entry Title', 'This is my newest blog entry') 
>>> ent.title
'Blog Entry Title'
>>> ent.text
'This is my newest blog entry'
  • This ensures that a blog entry has useful values when it's created.
  • However, it's no longer possible to create an empty entry.

You can't create an empty blog entry anymore:

>>> ent = BlogEntry() 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 2 required positional arguments: 'title' and 'text'

Example Code

class BlogPost : 
    def __init__(self, bl_title, bl_text) : 
        self.title = bl_title
        self.text = bl_text 
    def print_post(self) : 
        print ('Title:', self.title)
        print ('      ', self.text)
class Blogger :
    def __init__(self, login, name, email) :
        self.login = login = name = email 
        self.posts = [] 
    def new_post(self, title, text) :
        p = BlogPost(title, text)
    def print_posts(self) : 
        print ('The blogger',, 'has the posts:')
        for post in self.posts : 
class BlogSite : 
    def __init__(self, url) : 
        self.url = url 
        self.users = {} 
    def add_user(self, user) : 
        self.users[user.login] = user 
    def list_users(self) :
        l = []
        for user in self.users :
        return l 
    def user_summary(self, username) :
        l = []
        for post in self.users[username].posts :
        return l 
    def update_user_info(self, username, name, email) :
        if name is not None : 
            self.users[username].name = name 
        if email is not None :
            self.users[username].email = email 
blogger1 = Blogger('mike', 'Mike Matera', '')
blogger1.new_post('First Post', 'This is my first blog post!')
blogger1.new_post('Sunny Day', 'But the ice is slippery.')
site = BlogSite('')
site.update_user_info('mike', 'Mike M. Matera', 'mmmatera@cabrillo.ed')
titles = site.user_summary('mike')