Hour 11. Making Classes


What You’ll Learn in This Hour:

Image How to make a basic class statement

Image How to add methods to a class

Image How to set up a class instance

Image When to use classes in the real world


In the last hour, we dove into the theory of object-oriented programming and saw that the data types that come with Python are already objects. In this hour, we’ll make our own objects. For that, we need to use classes. Classes are what we use to create a blueprint for creating objects.

Making a Basic Class Statement

A class always has the same basic layout: It’s defined with a block of code underneath it that contains all the methods and attributes the class will contain:

>>> class MyClass(object):
...   a = 5
...   b = 2
...   c = "Hello"
...
>>>

To create a new instance of MyClass, you have to call the class by adding a pair of parentheses after the class name:

>>> class MyClass(object):
...   a = 5
...   b = 2
...   c = "Hello"
...
>>> new_item = MyClass()

Now that we have our new instance, new_item, we can see what’s in the attributes by adding their name in the class after the variable’s name:

>>> class MyClass(object):
...   a = 5
...   b = 2
...   c = "Hello"
...
>>> new_item = MyClass()
>>> new_item.a
5
>>> new_item.c
'Hello'

We can update the values in those attributes just like we’d update the values in any other variable:

>>> new_item.a
5
>>> new_item.a = 10
>>> new_item.a
10

Adding Methods to Classes

Attributes are only part of the story with classes: Functions are also incredibly powerful. Defining a method for a class looks very much like defining a function for a program, but with two changes: It’s going to be a block of code under the class (so even further indented), and it’s going to have at least one parameter: self.

Let’s take our class from the last section and add a simple function:

>>> class MyClass(object):
...     a = 5
...     b = 7
...     def print_a(self):
...        print "Hello! Here is a: {}".format(self.a)
...
>>> my_object = MyClass()
>>> my_object.print_a()
Hello! Here is a: 5

There are some subtle differences between calling a function as we normally would and calling a class function. First, the function’s name is attached to the end of the variable’s name. So, rather than calling do_something(), we call my_var.do_something().

Also, there appears at first glance to be a mismatch between how many parameters we’re sending and how many the method is expecting. We told the method to expect a value called self, but we never seem to send it. Why don’t we get an error? self is special: It means “everything included in the object.” self ends up being whatever object we used to call that function.

For example, let’s say we create two instances of a new class called School, which has some attributes for a school and a method used to print out some information about the school, as follows:

>>> class School(object):
...   name = ''
...   address = ''
...   type = 'grade school'
...   def print_school(self):
...     print self.name
...     print self.address
...     print "Type: " + self.type
...
>>> school1 = School()
>>> school2 = School()

Right now, the schools are exactly the same: no name, no address, and they have the 'grade school' type. Let’s change that:

>>> school1.name = "Wyland Elementary"
>>> school1.address = "100 Peachtree Ave Atlanta GA"
>>> school2.name = "George Mason University"
>>> school2.address = "300 University Way Fairfax VA"
>>> school2.type = "university"

Now, if we print both instances, we’ll see that they have completely different values stored in them:

>>> school1.print_school()
Wyland Elementary
100 Peachtree Ave
Atlanta GA
Type: grade school
>>> school2.print_school()
George Mason University
300 University Way
Fairfax VA
Type: university

Setting Up Class Instances

So far, we’ve been creating all the attributes in the first block under the class definition. There are some problems with this, however: It can get messy fast, and there’s no way to set the attributes when we create the new instance of the class. Also, there’s no way to do anything more subtle, such as run a certain function or get information from the user. Python gives us a better way to set up our class instances: __init__().

The __init__() Function

When you first create a new class instance, Python checks to see if you’ve defined an __init__() function. If you have, that function is run, as in the following example:

>>> class Student(object):
...   def __init__(self):
...     self.name = "None"
...     self.grade = "K"
...     self.district = "Orange County"
...
>>> student1 = Student()
>>> student1.name
'None'

Note that you have to define the attribute using self. At first, this may seem like the code is getting more complicated for no reason. Don’t forget, though, that we now have a function, and we can pass values to a function. Let’s rewrite Student so that we can pass values to it:

>>> class Student(object):
...   def __init__(self, name = "None", grade = "K", district = "Orange Country"):
...     self.name = name
...     self.grade = grade
...     self.district = district
...
>>> student1 = Student()
>>> student2 = Student(name = "Byron Blaze",
...                    grade = "12",
...                    district = "Fairfax County")
>>> student1.name
'None'
>>> student2.name
'Byron Blaze'

Being able to set up a new instance with the values is certainly useful, but it’s not all we can do. Never forget that __init__(), although a bit special, is still a function. In it, we can do anything we’d normally do in a function, such as call other functions, get input from the user, or perform some calculations. It’s a great place to make sure your data is clean or to get more nuanced input from the user. Consider the following code (stored in its own file):

class Student(object):

    def __init__(self, name="", school="", grade=""):
         if not name:
             name = raw_input("What is the student's name? ")
         if not school:
             school = raw_input("What is the student's school? ")
         if not grade:
             grade = self.get_grade()
         self.name = name
         self.school = school
         self.grade = grade
         self.print_student()

    def get_grade(self):
         while True:
             grade = raw_input("What is the student's grade? [K, 1-5] ")
             if grade.lower() not in ['k', '1', '2', '3', '4', '5']:
                 print "I'm sorry, but {} isn't valid.".format(grade)
             else:
                 return grade

    def print_student(self):
         print "Name: {}".format(self.name)
         print "School: {}".format(self.school)
         print "Grade: {}".format(self.grade)

def main():
     student1 = Student()
     student2 = Student(name="Byron Bale", grade="2", school="Minnieville")

if __name__ == "__main__":
     main()

If the user provides defaults, __init__() uses them. If the user doesn’t provide anything, however, __init__() gets the information it needs from the user, even checking to see if the grade is valid. At the end of __init__(), the student is printed out.

When we run the file, we get the following:

$ python student.py
What is the student's name? Billy Tate
What is the student's school? Midvale
What is the student's grade? [K, 1-5] 7
I'm sorry, but 7 isn't valid.
What is the student's grade? [K, 1-5] 5
Name: Billy Tate
School: Midvale
Grade: 5
Name: Byron Bale
School: Minnieville
Grade: 2

Moving and Storing Instances

Instances, just like strings, integers, and any other data type, can be passed to functions. Once you create an instance, you can move it and everything it contains around your program. You can also store instances in dictionaries and lists, just like the data types you’ve already learned about.

Let’s alter our student script from the last section so that we save students to a list and then print them out in a roster:

class Student(object):
    def __init__(self, name="", school="", grade=""):
        if not name:
            name = raw_input("What is the student's name? ")
        if not school:
            school = raw_input("What is the student's school? ")
        if not grade:
            grade = self.get_grade()
             self.name = name
        self.school = school
        self.grade = grade

    def get_grade(self):
        while True:
            grade = raw_input("What is the student's grade? [K, 1-5] ")
            if grade.lower() not in ['k', '1', '2', '3', '4', '5']:
                print "I'm sorry, but {} isn't valid.".format(grade)
            else:
                return grade

    def print_student(self):
         print "Name: {}".format(self.name)
         print "School: {}".format(self.school)
         print "Grade: {}".format(self.grade)

def print_roster(students):
    print "Students in the system:"
    for student in students:
        print "*"*15
        student.print_student()

def main():
     student1 = Student(name="Carrie Kale", grade="3", school="Marshall")
     student2 = Student(name="Byron Bale", grade="2", school="Minnieville")
     student3 = Student(name="Sarah Chandler", grade="K", school="Woodbridge")
     students = [student1, student2, student3]
     print_roster(students)

if __name__ == "__main__":
     main()

Note that we took the line that printed the student out of __init__() and that we’ve added a new function. This function accepts a list of students and prints out each one of them, using the print_student() function of the Student class.

Now, when we run the script, we get the following roster:

$ python ch12_student2.py
Students in the system:
***************
Name: Carrie Kale
School:
Marshall
Grade: 3
***************
Name: Byron Bale
School: Minnieville
Grade: 2
***************
Name: Sarah Chandler
School: Woodbridge
Grade: K

When we passed around the list of students, the students took with them all the information we’d stored in them, including their name, the name of their school, and their grade.

Using Classes in the Real World

Let’s return to our restaurant example and the manager’s menu program. He already has his plans for both the menu and menu item classes. Now he knows enough to code them! Because the menu contains menu items, it makes sense to start with menu items. The manager checks his plan, shown in Figure 11.1, and then starts coding.

Image

FIGURE 11.1 Menu item plan.

He remembers to use __init__() to set up his MenuItem object, although he decides to not have any defaults for the item’s title or cost. He also adds an option to his print_item() method that allows him to pick the long or short description. He also writes out some basic functions that let him change a menu item’s attributes:

class MenuItem(object):

  def __init__(self, title, cost, long_desc = '', short_desc = '', item_
  type='main'):
    self.title = title
    self.cost = cost
    self.long_desc = long_desc
    self.short_desc = short_desc
    self.item_type = item_type

  def change_item_type(self, item_type):
    self.item_type = item_type

  def change_cost(self, cost):
    self.cost = cost

  def change_description(self, long_desc='', short_desc=''):
    if long_desc:
      self.long_desc = long_desc
    if short_desc:
      self.short_desc = short_desc

  def change_title(self, title):
    self.title = title

  def print_item(self, desc_type='short'):
    print "{title} ... ${cost}".format(title=self.title, cost=self.cost)
    if desc_type == 'short':
      print self.short_desc
    elif desc_type == 'long':
      print self.long_desc

Now that he has his MenuItem class, he checks his plan for the menu object (see Figure 11.2).

Image

FIGURE 11.2 Menu object plan.

He realizes he might need to research a bit more in order to print the various menus. The online one should be a web page, and he hasn’t learned anything about that yet. That doesn’t mean he can’t set up the menu object, though. In the same file as MenuItem, he writes this code:

class Menu(object):

  def __init__(self, breakfast, lunch, dinner):
    self.breakfast = breakfast
    self.lunch = lunch
    self.dinner = dinner

  def print_menu(self):
    print "Breakfast"
    for item in self.breakfast:
      item.print_item()
    print
    print "Lunch"
    for item in self.lunch:
      item.print_item()
    print
    print "Dinner"
    for item in self.dinner:
      item.print_item()

The Menu class has an __init__ function that sets up three attributes. It also has a function that prints out a basic menu by printing the meal heading and then all the items in that category.

Though he doesn’t have everything coded out yet, he feels this is a pretty good start to creating a menu program.

Summary

During this hour, you learned how to create a class so that you can make objects in Python. You also learned how to set and change attributes, as well as create class methods. Finally, you learned how to use __init__() to automatically set up the instance for a class when it’s created.

Q&A

Q. Why does __init__() have those underscores?

A. There are many functions that are never meant to be called directly. For example, you would never say MyClass.__init__(). You would say MyClass(). Python already knows to call __init__().

Q. Are there other special functions like __init__()?

A. There are! We’ll go over many of them in Hour 12, “Expanding Classes to Add Functionality.” Many of them are used to give more power to the developer when creating class. For example, one allows you to customize what’s printed if you print your object. Others allow you to use math operators on your objects.

Q. Why do I have to add (object) after the class name?

A. You don’t have to, but it’s strongly recommended. First, this is the modern standard for defining classes (they’re even called “new style classes”). Second, it gives your classes some extra features that you may not need now but might need in the future. You might as well get into the habit now!

Workshop

The Workshop contains quiz questions and exercises to help you solidify your understanding of the material covered. Try to answer all questions before looking at the answers that follow.

Quiz

1. How do you create a new instance of a class?

2. How do you call a class’s method? How do you get the value out of an attribute?

3. Why do we use self in a class?

4. What method is called when a new instance is created?

Answers

1. To create a new instance of a class, you call the class like this:

my_variable = NewClass()

2. To call a method, you use the method name tacked onto the end of the variable like this:

my_variable.method_name()

To get a value from an attribute, use the variable name followed by the attribute name:

my_variable.some_value

3. self is what we use to let Python know we want to work with the attributes and methods stored in the current object, and not some new variable.

4. Every time a new instance of an object is created, __init__() is called. If the developer hasn’t created an __init__() method, nothing happens.

Exercise

If you take a look at the code for the student list example, you’ll notice that there’s only error checking if the user inputs the data through raw_input(). A student object can be created with bad data if the user sends bad data through the parameters! Add code so that the grade is checked no matter how __init__() gets that value.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.182.76