Class Coding Methods

In Python terms, we want to code operations on objects in a class’s methods, instead of littering them throughout our program. In fact, this is one of the things that classes are very good at—factoring code to remove redundancy and thus optimize maintainability

This is all simpler in code than it may sound in theory. The following achieves encapsulation by moving the two operations from code outside the class to methods inside the class. While we’re at it, let’s change our self-test code(from previous chapter) at the bottom to use the new methods we’re creating, instead of hardcoding operations:

class Person:   #start a class
    def __init__(self, name, job=None, pay=0):    # Constructor takes three arguments
        self.name = name    # Fill out fields when created
        self.job = job     # self is the new instance object
        self.pay = pay
    def lastname(self):
        return self.name.split()[-1]
    def giveraise(self, percent):
        self.pay = int(self.pay * (1 + percent))
if __name__ == '__main__':
    john = Person("John Cena")
    aman = Person("Aman Kharwal", job='Programmer', pay=500000)
    print(aman.name, aman.pay)
    print(john.name, john.pay)
    print(john.lastname(), aman.lastname())
    aman.giveraise(.10)
    print(aman.pay)

#Output-

Aman Kharwal 500000
John Cena 0
Cena Kharwal
550000

As we’ve learned, methods are simply normal functions that are attached to classes and designed to process instances of those classes. The instance is the subject of the method call and is passed to the method’s self argument automatically.

A few coding details are worth pointing out here. First, notice that aman’s pay is now still an integer after a pay raise—we convert the math result back to an integer by calling the int built-in within the method. Changing the value to either int or float is probably not a significant concern for this demo: integer and floating-point objects have the same interfaces and can be mixed within expressions. Still, we may need to address truncation and rounding issues in a real system—money probably is significant to Persons!

Step 3: Operator Overloading

The __init__ constructor method we’ve already coded is, strictly speaking, operator overloading too—it is run automatically at construction time to initialize a newly created instance. Constructors are so common, though, that they almost seem like a special case. More focused methods like __repr__ allow us to tap into specific operations and provide specialized behavior when our objects are used in those contexts.

Let’s put this into code. The following extends our class to give a custom display that lists attributes when our class’s instances are displayed as a whole, instead of relying on the less useful default display:

class Person:   #start a class
    def __init__(self, name, job=None, pay=0):    # Constructor takes three arguments
        self.name = name    # Fill out fields when created
        self.job = job     # self is the new instance object
        self.pay = pay
    def lastname(self):
        return self.name.split()[-1]
    def giveraise(self, percent):
        self.pay = int(self.pay * (1 + percent))
    def __repr__(self):    # Added method
        return '[Person:%s, %s]'% (self.name, self.pay)    #String to print
if __name__ == '__main__':
    john = Person("John Cena")
    aman = Person("Aman Kharwal", job='Programmer', pay=500000)
    print(aman)
    print(john)
    print(john.lastname(), aman.lastname())
    aman.giveraise(.10)
    print(aman)

#Output-

[Person:Aman Kharwal, 500000]
[Person:John Cena, 0]
Cena Kharwal
[Person:Aman Kharwal, 550000]

Notice that we’re doing string % formatting to build the display string in __repr__ here; at the bottom, classes use built-in type objects and operations like these to get their work done. Again, everything you’ve already learned about both built-in types and functions applies to class-based code. Classes largely just add an additional layer of structure that packages functions and data together and supports extensions.

Step 4: Coding Subclasses

As a next step, then, let’s put OOP’s methodology to use and customize our Person class by extending our software hierarchy. For the purpose of this tutorial, we’ll define a subclass of Person called Manager that replaces the inherited giveraise method with a more specialized version. Our new class begins as follows:

class Manager(Person):

This code means that we’re defining a new class named Manager, which inherits from and may add customizations to the superclass Person.

For the sake of argument, let’s assume that when a Manager gets a raise, it receives the passed-in percentage as usual, but also gets an extra bonus that defaults to 10%. For instance, if a Manager’s raise is specified as 10%, it will really get 20%. (Any relation to Persons living or dead is, of course, strictly coincidental.) Our new method begins as follows; because this redefinition of giveraise will be closer in the class tree to Manager instances than the original version in Person, it effectively replaces, and thereby customizes, the operation.

class Manager(Person):
    def giveraise(self, percent, bonus=.10):
        

What we really want to do here is somehow augment the original giveraise, instead of replacing it altogether. The best way to do that in Python is by calling to the original version directly, with augmented arguments, like this:

class Manager(Person):
    def giveraise(self, percent, bonus=.10):
        Person.giveraise(self, percent + bonus)

Here’s our entire module file with this step applied:

class Person:   #start a class
    def __init__(self, name, job=None, pay=0):    # Constructor takes three arguments
        self.name = name    # Fill out fields when created
        self.job = job     # self is the new instance object
        self.pay = pay
    def lastname(self):
        return self.name.split()[-1]
    def giveraise(self, percent):
        self.pay = int(self.pay * (1 + percent))
    def __repr__(self):    # Added method
        return '[Person:%s, %s]'% (self.name, self.pay)    #String to print
class Manager(Person):
    def giveraise(self, percent, bonus=.10):
        Person.giveraise(self, percent + bonus)
if __name__ == '__main__':
    john = Person("John Cena")
    aman = Person("Aman Kharwal", job='Programmer', pay=500000)
    print(aman)
    print(john)
    print(john.lastname(), aman.lastname())
    aman.giveraise(.10)
    print(aman)
    alex = Manager("Alex Hales", "Manager", 40000)
    alex.giveraise(.10)
    print(alex.lastname())
    print(alex)

#Output-

[Person:Aman Kharwal, 500000]
[Person:John Cena, 0]
Cena Kharwal
[Person:Aman Kharwal, 550000]
Hales
[Person:Alex Hales, 48000]

That’s it for today we will continue in the next Chapter.

Previous Chapter