Problem Solving with Algorithms and Data Structures using Python
Section 1.13: https://runestone.academy/ns/books/published/pythonds/Introduction/ObjectOrientedProgramminginPythonDefiningClasses.html
Section 2.1: https://runestone.academy/ns/books/published/pythonds/ProperClasses/a_proper_python_class.html
Section 2.2 appears to be missing from the book, but we'll cover that too
Last time, we made a class for representing measurements in feet-inches.
We just implemented the special __add__() method, which will make it work with the + operator.
class FeetInches:
def __init__(self,f,i):
self.feet = f
self.inches = i
def simplify(self):
"""
if the number of inches is > 12,
this regroups the excess into feet
"""
self.feet += self.inches // 12
self.inches = self.inches % 12
def __repr__(self):
return str(self.feet)+"ft. "+str(self.inches)+"in."
def __add__(self,other_measurement):
total_feet = self.feet + other_measurement.feet
total_inches = self.inches + other_measurement.inches
#create an new FeetInches object with the new measurements
total_FI = FeetInches(total_feet,total_inches)
total_FI.simplify()
return total_FI
measurement1 = FeetInches(3,6)
measurement2 = FeetInches(2,6)
total = measurement1 + measurement2
print(total)
6ft. 0in.
Similarly, you can support any of the following operators (plus more that I haven't listed):
+: object.__add__(self, other) -: object.__sub__(self, other) *: object.__mul__(self, other) /: object.__truediv__(self, other) //: object.__floordiv__(self, other)%: object.__mod__(self, other)**: object.__pow__(self, other)<<: object.__lshift__(self, other)>>: object.__rshift__(self, other)&: object.__and__(self, other) (this is not the logical and operator)^: object.__xor__(self, other)|: object.__or__(self, other) (this is not the logical or operator)<: object.__lt__(self, other)<=: object.__le__(self, other)==: object.__eq__(self, other)!=: object.__ne__(self, other)>: object.__gt__(self, other)>=: object.__ge__(self, other)Add support for the subtraction operator to the FeetInches class.
Discussion the following questions.
__add__ is for +) to be able to compare two FeetInches objects like in the code below?__add__ and __sub__ both return a new object of type FeetInches. What type of value should the function for < return?measurement1 = FeetInches(3,6)
measurement2 = FeetInches(2,6)
print(measurement1 < measurement2)
Add support for the < (less than) operator to the FeetInches class.
After you implement <, you might get some other operators like > for free. Try the code below and see if it works with your definition. Do <=, ==, !=, >= work? Why or why not?
measurement1 = FeetInches(3,6)
measurement2 = FeetInches(2,6)
print(measurement1 > measurement2)
We'll use this space to go over the solution to the group activities above.
class FeetInches:
def __init__(self,f,i):
self.feet = f
self.inches = i
def simplify(self):
"""
if the number of inches is > 12,
this regroups the excess into feet
"""
self.feet += self.inches // 12
self.inches = self.inches % 12
def __repr__(self):
return str(self.feet)+"ft. "+str(self.inches)+"in."
def __add__(self,other_measurement):
total_feet = self.feet + other_measurement.feet
total_inches = self.inches + other_measurement.inches
#create an new FeetInches object with the new measurements
total_FI = FeetInches(total_feet,total_inches)
total_FI.simplify()
return total_FI
measurement1 = FeetInches(3,6)
measurement2 = FeetInches(2,6)
total = measurement1 + measurement2
print(total)
Let's say we want to create a class for representing playing cards (so we can implement games like War, Bridge, Black Jack, etc.).
Here is a bare-bones implementation:
class PlayingCard:
def __init__(self,v,s):
self.value = v
self.suit = s
two_of_clubs = PlayingCard(2,"♣")
two_of_hearts = PlayingCard(2,"♡")
ten_of_hearts = PlayingCard(10,"♡")
seven_of_spades = PlayingCard(7,"♠")
four_of_diamonds = PlayingCard(4,"♢")
print("Here's what the card looks like:",ten_of_hearts)
Here's what the card looks like: <__main__.PlayingCard object at 0x107713370>
Note that ♣, ♡, ♠, and ♢ are all just text characters (see https://en.wikipedia.org/wiki/Playing_cards_in_Unicode ). You can use C, H, S, and D if you prefer.
How should we handle cards without a numic value like Queens or Aces?
What do we have to do to get the cards to print nicely?
What do we have to do to allow us to write code like this?
Copy the code above and begin adding support for these.
if two_of_clubs < ten_of_hearts:
print("Player 2 wins the hand")
When designing a class, you should plan for how to protect against accidental misuse.
For example, what happens if someone tries to do this?
pikachu = PlayingCard("Pikachu",40)
This particular class is only meant to represent standard French playing cards, so this should cause some kind of error.
One way to handle it might be like this:
class PlayingCard:
def __init__(self,v,s):
if type(v) != type(1) or v > 14 or v < 2:
raise Exception("A PlayingCard's value must be an integer in the range 2-14.")
self.value = v
self.suit = s
twentyseven_of_clubs = PlayingCard(27,"♣")
pikachu = PlayingCard("Pikachu",40)
--------------------------------------------------------------------------- Exception Traceback (most recent call last) Input In [2], in <cell line: 10>() 7 self.value = v 8 self.suit = s ---> 10 twentyseven_of_clubs = PlayingCard(27,"♣") 11 pikachu = PlayingCard("Pikachu",40) Input In [2], in PlayingCard.__init__(self, v, s) 3 def __init__(self,v,s): 5 if type(v) != type(1) or v > 14 or v < 2: ----> 6 raise Exception("A PlayingCard's value must be an integer in the range 2-14.") 7 self.value = v 8 self.suit = s Exception: A PlayingCard's value must be an integer in the range 2-14.
By convention, any attribute or method whose name starts with an underscore should be treated as private, meaning you shouldn't change the variable outside the class.
self._value
If it doesn't start with an underscore, it is public and changing it outside the class is fair game.
If you start it with two underscores, then Python performs name mangling, which doesn't let you change the name outside the class (unless you do something extra to get around the mangling).
self.__value
class PlayingCard:
def __init__(self,v,s):
if type(v) != type(1) or v > 14 or v < 2:
raise Exception("A PlayingCard's value must be an integer in the range 2-14.")
self.__value = v
self.__suit = s
def __repr__(self):
return str(self.__value)+str(self.__suit)
pikachu = PlayingCard(2,"♣")
pikachu.__value = "Pikachu" #this doesn't actually change self.__value
pikachu.__suit = 40
print(pikachu)
2♣
Update your class so that self.value and self.suit are hidden (private/mangled).