Object-Oriented Programming
Wednesday, 7/15
Announcements
Object-Oriented Programming
Review: Functions
Functions
describing computations
Review: Data
Functions
describing computations
Data
Storing information
Review: State
Functions
describing computations
Data
Storing information
State
Changing information over time
Object-Oriented Programming
Life with data abstraction
def rational(num, denom):
return [num, denom]
def numer(r):
return r[0]
def denom(r):
return r[1]
constructor
selectors
Life with data abstraction
Data abstraction
Can we do better?
Good idea:
easier to read and maintain code
Consequences:
prone to abstraction violations
Object-Oriented Programming
Object-Oriented Programming
Name: Albert
Favorite food: burgers
walk to a destination
Attributes
Actions
pet an animal
Human
Name: Sally
Favorite food: fries
climb a tree
Attributes
Actions
pose for pictures
Squirrel
Object-Oriented Programming
Albert is a human
Sally is a squirrel
object
class
Object-Oriented Programming
Object-Oriented Programming: learning by example
Acknowledgements: Tom Magrino and Jon Kotker developed the original Pokemon example for CS 61A, Summer 2012
Classes and objects
class Pokemon:
...
"Here's what a Pokemon looks like!"
Classes and objects
class Pokemon:
def __init__(self, name):
...
"I want to create my own Pokemon!"
Classes and objects
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
self.hp = 5 * self.level
self.damage = 2 * self.level
"When I create my Pokemon, I'll give it a name and it'll start at level 1"
Instance attributes
self.level might change later!
Classes and objects
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
self.hp = 5 * self.level
self.damage = 2 * self.level
Pika!
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu
<Pokemon object at …>
Object-Oriented Programming
Attributes: Dot notation
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu.name
'pikachu'
>>> ashs_pikachu.level
1
>>> ashs_pikachu.hp
5
ashs_pikachu.name
object
attribute
"The name of ashs_pikachu"
Attributes: Instance attributes
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu.name
'pikachu'
>>> alberts_jigglypuff = Pokemon('jigglypuff')
>>> alberts_jigglypuff.name
'jigglypuff'
Jiggly!
"The name of ashs_pikachu is different from the name of alberts_jigglypuff"
Attributes: self
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
self.hp = 5 * self.level
self.damage = 2 * self.level
self is a variable that is bound to an instance of this class
From Pikachu's point of view:
"My name will be 'pikachu', and I will start at level 1"
Attributes: State
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu.level
1
>>> ashs_pikachu.level += 9000
>>> ashs_pikachu.level
9001
"ashs_pikachu's leveled up!"
Same expression, different values!
Attributes: State
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu.status = 'confused'
>>> ashs_pikachu.status
'confused'
"ashs_pikachu's is confused!"
Creates new instance attribute
Attributes: Class attributes
class Pokemon:
population = 0
def __init__(self, name):
self.name = name
self.level = 1
self.hp = 5 * self.level
self.damage = 2 * self.level
Pokemon.population += 1
>>> Pokemon.population
0
>>> ashs_pikachu = Pokemon('pikachu')
>>> Pokemon.population
1
>>> alberts_jigglypuff = Pokemon('jigglypuff')
>>> Pokemon.population
2
Class attribute: shared by all objects of the same class
Attributes: Attribute lookup
>>> ashs_pikachu = Pokemon('pikachu')
>>> Pokemon.population
1
>>> ashs_pikachu.population
1
Pokemon
population: 0
ashs_pikachu
name: 'pikachu'
level: 1
hp: 5
damage: 2
Pokemon
population: 1
Attributes: Attribute lookup
>>> ashs_pikachu = Pokemon('pikachu')
>>> Pokemon.population += 150
>>> ashs_pikachu.population
151
ashs_pikachu
name: 'pikachu'
level: 1
hp: 5
damage: 2
Pokemon
population: 1
Pokemon
population: 151
Attributes: Attribute lookup
>>> ashs_pikachu = Pokemon('pikachu')
>>> Pokemon.population += 150
ashs_pikachu
name: 'pikachu'
level: 1
hp: 5
damage: 2
>>> ashs_pikachu.population -= 126
>>> ashs_pikachu.population
25
>>> Pokemon.population
151
ashs_pikachu
name: 'pikachu'
level: 1
hp: 5
damage: 2
population: 25
Pokemon
population: 151
ashs_pikachu.population = ashs_pikachu.population - 126
Evaluated first: 151
Assigned to 25
Object identity and type checking
>>> ashs_pikachu = Pokemon('pikachu')
>>> alberts_jigglypuff = Pokemon('jigglypuff')
>>> ashs_pikachu is alberts_jigglypuff
False
>>> roberts_pikachu = ashs_pikachu
>>> ashs_pikahu is roberts_pikachu
True
>>> roberts_pikachu = Pokemon('pikachu')
>>> ashs_pikachu is roberts_pikachu
False
>>> isinstance(ashs_pikachu, Pokemon)
True
>>> type(ashs_pikachu)
<class 'Pokemon'>
(demo)
Object-Oriented Programming
Methods: Your turn!
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
self.hp = 5 * self.level
self.damage = 2 * self.level
def talk(self):
print(self.name + '!')
"When a Pokemon talks, it prints its name excitedly!"
self is just a variable. We can technically call it anything. It is called self by convention
Methods: Usage
class Pokemon:
...
def talk(self):
print(self.name + '!')
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu.talk()
pikachu!
>>> ashs_pikachu.talk(ashs_pikachu)
TypeError
Methods: Methods vs. Functions
class Pokemon:
...
def talk(self):
print(self.name + '!')
>>> ashs_pikachu = Pokemon('pikachu')
>>> ashs_pikachu.talk()
pikachu!
>>> Pokemon.talk(ashs_pikachu)
pikachu!
Method call: bound to an object
Function call: not bound
(demo)
Python distinguishes between
Methods: Your turn!
class Pokemon:
...
def decrease_hp(self, amount):
"*** YOUR CODE HERE ***"
def attack(self, other):
"*** YOUR CODE HERE ***"
Methods
class Pokemon:
...
def decrease_hp(self, amount):
self.hp -= amount
if self.hp < 0:
self.hp = 0
def attack(self, other):
print(self.name,
'attacks',
other.name)
other.decrease_hp(self.damage)
Object-Oriented Programming
Property methods
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
self.hp = 5 * self.level
self.damage = 2 * self.level
def level_up(self):
self.level += 1
self.hp = 5 * self.level
self.damage = 2 * self.level
>>> ashs_pikachu.level_up()
>>> ashs_pikachu.level
2
>>> ashs_pikachu.hp
10
>>> ashs_pikachu.level = 5
>>> ashs_pikachu.hp
10
Have to duplicate code for assigning hp and damage
Property methods
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
def level_up(self):
self.level += 1
@property
def hp(self):
return 5 * self.level
@property
def damage(self):
return 2 * self.level
>>> ashs_pikachu.level_up()
>>> ashs_pikachu.level
2
>>> ashs_pikachu.hp
10
>>> ashs_pikachu.level = 5
>>> ashs_pikachu.hp
25
Property method:
technically a method, but acts like an attribute
No parentheses!
Property methods
>>> ashs_pikachu.level_up()
>>> ashs_pikachu.level
2
>>> ashs_pikachu.hp = 9001
AttributeError
>>> ashs_pikachu.level_up()
>>> ashs_pikachu.level
2
>>> ashs_pikachu.hp = 9001
>>> ashs_pikachu.hp
9001
Can't assign to a property method
Instance attribute
Property method
Object-Oriented Programming
Private variables (sort of)
class Pokemon:
def __init__(self, name):
self.name = name
self.level = 1
self.__secret = 42
def reveal_secret(self):
return self.__secret
>>> ashs_pikachu.__secret
AttributeError
>>> ashs_pikachu._Pokemon__secret
42
>>> ashs_pikachu.reveal_secret()
42