# LibraryBook is the name of the class
class LibraryBook:
pass # pass indicates that the body/suit of the class definition is empty.
# This will create an instance of the class.
my_book = LibraryBook()
my_book
## <__main__.LibraryBook object at 0x1096ff730>
type(my_book)
# Another way to check the type of some object
## <class '__main__.LibraryBook'>
isinstance(my_book, LibraryBook)
## True
Why use classes and when?? Objects simplify problems by providing an abstraction over certain data types and their functionality. Instead of thinking of the problem in terms of individual strings, integers, etc. we can now think in terms of LibraryBooks (or other objects).
Data fields - Each instance owns its own data (the class can define what names the data fields have).
The init(self, …) method is automatically called by Python when a new instance is created. This method is called the class constructor; it initialize the data values in the class.
class LibraryBook (object):
def __init__(self, title, author, pub_year, call_no, checked_out):
self.title = title
self.author = author
self.year = pub_year
self.call_number = call_no
self.checked_out = checked_out
# The self parameter is REQUIRED within the class, because it tells the program to retrieve/act on the instance object.
# Since we have already created my_book as a LibraryBook object, we could now manually add the title, author,... information associated with the book.
my_book.title = "The Nature and Origins of Mass Opinion"
my_book.author = ('Zaller', 'John')
my_book.year = 1992
# Retrieve a specific data field of an instance by calling instance name and the field name
my_book.title
## 'The Nature and Origins of Mass Opinion'
# We could pass all the information into the __init__ to set up the fields when creating the new instance.
new_book = LibraryBook("The Rationalizing Voter", ("Lodge","Milt"), 2013, "PZ7.R79999", True)
new_book.title
## 'The Rationalizing Voter'
new_book.checked_out
## True
Methods contain the functionality of the object.These are defined in the class.
class LibraryBook(object):
def __init__(self, title, author, pub_year, checked_out):
self.title = title
self.author = author
self.year = pub_year
self.checked_out = checked_out
### Methods for LibraryBook
# Returns the title and author information of the book as a string
def title_and_author(self):
return "{} {}: {}".format(self.author[1], self.author[0], self.title)
# Prints all information associated with a book in this format
def __str__(self): # make sure that __str__ returns a string!
return "{} {} ({}): {}".format(self.author[1], self.author[0], self.year, self.title)
# Returns a string representation of the book with it' title and checked_out status
def __repr__(self):
return "<Book: {} ({})>".format(self.title, self.checked_out)
# Simply calling the instance itself is triggering __repr__()
new_book
## <__main__.LibraryBook object at 0x109724430>
# print is triggering the __string__()
print(new_book)
## <__main__.LibraryBook object at 0x109724430>
new_book = LibraryBook("The Rationalizing Voter", ("Lodge","Milt"), 2013, "True")
new_book.title_and_author()
## 'Milt Lodge: The Rationalizing Voter'
The ONLY difference is:
Example of instance-of relationship.
nemo is an instance of ClownFish.
class ClownFish(object):
pass
nemo = ClownFish()
type(nemo)
## <class '__main__.ClownFish'>
isinstance(nemo, ClownFish)
## True
But ClownFish is also a fish, a vertebrate, and an animal, and each could a separate class. In this case, we need to have relationships between class.
The ClownFish class could have the parent class Fish,
which could have a parent class Vertebrate,
which could have a parent class Animal…
This relationship holds between a child class and its parent class. Every class in Python has at least one parent class. Note that the is-a relationship is transitive, so every ClownFish is also an Animal.
There is a top-most class in Python called object. So far, when we defined classes, we always made object the direct parent of the class.
class Animal(object): pass
class Vertebrate(Animal): pass
class Fish(Vertebrate): pass
class ClownFish(Fish): pass
class TangFish(Fish): pass
nemo = ClownFish()
isinstance(nemo, ClownFish)
## True
# TangFish is not a parent class of ClownFish
isinstance(nemo, TangFish)
# the is-a relationship is transitive
## False
isinstance(nemo, Animal)
# All classes have a parent class of Object.
## True
isinstance(nemo, object)
## True
Why use inheritance? Every class also has access to the class attributes of the parent class. In particular, methods defined on the parent class can be called on instances of their “decendants”.
class Fish(Animal):
def speak(self):
return "Blub blub blub"
class ClownFish(Fish): pass
class TangFish(Fish): pass
TangFish is a child class of Fish, so it can access the speak() from Fish class. It will first look for the method call within its class, and if not found, then repeat the search for each parent level up.
dory = TangFish()
dory.speak()
## 'Blub blub blub'
ClownFish is a child class of Fish, so it can access the speak() from Fish class
nemo = ClownFish()
nemo.speak()
## 'Blub blub blub'
What if we want different functionality for a child class? We can override the method (by writing a new one with the same name).
class TangFish(Fish):
def speak(self):
return "Hello, I'm a TangFish instance."
dory = TangFish()
dory.speak() # this speak() is from the TangFish class
## "Hello, I'm a TangFish instance."
nemo = ClownFish()
nemo.speak()
## 'Blub blub blub'
In a is-a relationship, the child classe could access the parent class’s attributes if not defined in the child class, or override the attribute value of same attribute exists in the child class.
However, if an instance is defined at one of the parent class levels, then it could NOT access the attributes that are defined in any of the lower child class level.
class Fish(Vertebrate):
# self.name is not defined in Fish class, but is defined in the ClownFish class.
def __str__(self):
return "Hello, my name is {}".format(self.name)
class ClownFish(Fish):
def __init__(self, name):
self.name = name
nemo = ClownFish("nemo")
# The self.name attribute for the __str__() is from the ClownFish class
# but the __str__() is from the Fish class
print(nemo)
## Hello, my name is nemo
class Fish(Vertebrate):
def __init__(self, name):
self.name = name
# self.name is not defined in Fish class, but is defined in the ClownFish class.
def __str__(self):
return "Hello, my name is {}".format(self.name)
class ClownFish(Fish):
def __init__(self, name):
self.name = name
nemo = ClownFish("Nemo")
print(nemo) # __str__() is accessing the self.name from the child level
## Hello, my name is Nemo
nemo = Fish("clown_fish") # __str__ ia accessing the self.name attribute from Fish class
print(nemo)
## Hello, my name is clown_fish