Python Tales - The Python Tutor Blog

A Python Programming Blog, from a Pythoneer to Pythoneers, created by The Python Tutor.

Saturday, July 22, 2017

A Python Tale on Object Oriented Programming (Part II)

Hello my friends, today’s entry is going to be the second part about Object Oriented Programming with Python, if you haven't read the first part of this set please go to with this link, now let's create a table to summarize what we have so far of our Clock Class.


Properties
Methods
·         Seconds
·         Tick
·         getHours
·         getMinutes
·         getSeconds
·         setHours
·         setMinues
·         setSeconds
·         setTime
·         getTime
·         reset

Now for this second part we are going to override some of the built-in Python methods on objects so we can use operators and some helpful functions. The first ones are those methods that will allow us to use the logical operators.
Operator
Description
Method
·         ==
·         !=
·      Equal
·      Not equal
·         __eq__
·         __ne__

When we are using theses operators on the construction of conditions for flow control (if-else, while) statements, we can use objects as the variables in the comparisons, but is this methods are not coded in the Class of the object, the execution will crash, so let's start by coding this examples using our Clock Class template, for simplicity in the code the methods already described in the first part will not be showed in this part.
class Clock(object):

    def __init__(selfhours=0, minutes=0seconds=0):

        self.seconds = 0

        self.seconds += seconds
        self.seconds += minutes * 60
        self.seconds += hours * 3600
 
    def Tick(self, seconds=1):
 
        self.seconds += seconds

    def __eq__(selfother):

        """Return True if seconds are equal."""

        return self.seconds == other.seconds

    def __ne__(selfother):

        """Return True if seconds are not equal."""

        return self.seconds != other.seconds

As you can see in this code, these methods usually uses the same format of double underscore, the keywords eq and ne mean equal and not equal, in the definition of these methods the self parameter must be provided as the first argument followed by a name variable referencing the other object passed for comparison, it is recommended to use other for readability. Then you need to return a Boolean type, this can be directly achieved by returning a comparison of the data that actually is being used for the purpose of comparison, in this case is the seconds instance variable, in the previous part was not clear why to use only one variable for the Clock representation instead of one variable for each of seconds, minutes and hours, now it is more clear because we can have a smaller code in these methods between others advantages, let's code a little using this methods for comparisons.
>>> clock1 = Clock(6, 0, 0)
>>> clock2 = Clock(5, 30, 0)
>>> print clock1
06:00:00
>>> print clock2
05:30:00
>>> print clock1 == clock2
False
>>> print clock1 != clock2
True
>>> clock2.Tick(1800)
>>> print clock1 == clock2
True
>>> print clock1 != clock2
False

In this ways there is no need to use the public interface of the Class to access the seconds data for making comparisons, you can make these comparisons by just using these methods and the comparison operators, let's code the rest of these methods and operators
Operator
Description
Method
·         < 
·         <=
·         > 
·         >=
·         Less than
·         Less or equal than
·         Greater than
·         Greater or equal than
·         __lt__
·         __le__
·         __gt__
·         __ge__
And their respective code
class Clock(object):
 
    def __init__(self, hours=0, minutes=0, seconds=0):
 
        self.seconds = 0
 
        self.seconds += seconds
        self.seconds += minutes * 60
        self.seconds += hours * 3600
 
    def Tick(self, seconds=1):
 
        self.seconds += seconds
 
    def __eq__(self, other):
 
        """Return True if seconds are equal."""
 
        return self.seconds == other.seconds
 
    def __ne__(self, other):
 
        """Return True if seconds are not equal."""
 
        return self.seconds != other.seconds
 
    def __lt__(self, other):
 
        """ Return True if rank of self < seconds of other. """
 
        return self.seconds < other.seconds
 
    def __le__(self, other):
 
        """ Return True if rank of self <= seconds of other. """
 
        return self.seconds <= other.seconds
 
    def __gt__(self, other):
 
        """ Return True if rank of self > seconds of other. """
 
        return self.seconds > other.seconds
 
    def __ge__(self, other):
 
        """ Return True if rank of self >= seconds of other. """
 
        return self.seconds >= other.seconds
    
    def __str__(self):
 
        result = "{0:02d}:{1:02d}:{2:02d}"
 
        return  result.format(self.getHours(), self.getMinutes(), self.getSeconds())

Let's do some basic examples with these
>>> clock1 = Clock(6, 25, 0)
>>> clock2 = Clock(8, 15, 0)
>>> print clock1, clock2
06:25:00 08:15:00
>>> clock1 > clock2
False
>>> clock1 < clock2
True
>>> clock1 <= clock2
True
>>> clock1.Tick(6600)
>>> clock1 < clock2
False
>>> clock1 > clock2
False
>>> clock1 >= clock2
True

Pretty cool ha? If you go to the Python official documentation on operators, you will see the entire list of operators and how to code your objects to have compatibility with them, but sometimes the nature of your objects has no real meaning for some operators, for instance you can use the addition operator +, for integers but what about Clock instances? Well you can, but adding both seconds and produce a new object with this result, but what about division? Can you divide one Clock with other Clock? Does this question even has sense?, well before we get philosophical let's code the addition operator (__add__).
class Clock(object):
 
    def __init__(self, hours=0, minutes=0, seconds=0):
 
        self.seconds = 0
 
        self.seconds += seconds
        self.seconds += minutes * 60
        self.seconds += hours * 3600
 
    def Tick(self, seconds=1):
 
        self.seconds += seconds
 
.
.
.
.
    
    def __add__(self, other):
 
        """ Return Clock instance with self.seconds + other.seconds """
 
        result = Clock()
        result.seconds = self.seconds + other.seconds
 
        return result
 
    def __str__(self):
 
        result = "{0:02d}:{1:02d}:{2:02d}"

And let's try an example of this.
>>> clock1 = Clock(10, 25, 0)
>>> clock2 = Clock(2, 10, 0)
>>> clock3 = clock1 + clock2
>>> print clock3
12:35:00

And that's it, we have covered enough for now, so try building your own Python Classes, I can give you some hints on prototypes, a Card Class and Deck Class is really useful for programming Card Games. , I hope you have found this entry helpful to you. So thanks for coming by my new entry and I hope you have enjoyed this as much as I enjoyed writing it, stop by the comments if you want to discuss about this. Don't forget to share this with your Python Peers. Cheers my friends.