6. Object-Oriented Programming in Python

About this Tutorial –

Objectives –

Python is a powerful and popular object-oriented scripting language. This course provides a comprehensive introduction to the core syntax and functions provided by Python, including full coverage of its object-oriented features. The course also explores some of Python’s powerful APIs and techniques, including file handling, XML processing, object serialization, and Web service

Audience

This training course is aimed at new developers and experienced developers

Prerequisites

No previous experience in Python programming is required. But any experience you do have in programming will help. Also no experience in IDLE is required. But again any experience you do have with programming development environments will be a valuable.

Contents

The python course covers these topics and more:

  • Strings and Regular Expressions: Overview of strings in Python; Basic string manipulation; Introduction to regular expressions
  • XML Processing: XML essentials; Parsing XML documents; Searching for XML content; Generating XML data
  • Web Services: Overview of Web services; Implementing Web services using Python; Caching; Compression; Handling redirects

Download Solutions

HTML tutorial


Overview

Estimated Time – 1 Hour

Not what you are looking? Try the next tutorial – Additional Object-Oriented Techniques

Lab 1: Essential concepts

Lab 1: Essential concepts
  1. What is a Class?
    • A class is a representation of a real-world entity
      • Defines data, plus methods to work on that data
      • You can hide data from external code, to enforce encapsulation
    • Domain classes
      • Specific to your business domain
      • E.g. BankAccount, Customer, Patient, MedicalRecord
    • Infrastructure classes
      • Implement technical infrastructure layer
      • E.g. NetworkConnection, AccountsDataAccess, IPAddress
    • Exception classes
      • Represent known types of error
      • E.g. Exception, BankException, CustomerException
    • Etc…
  2. What is an Object?
    • An object is an instance of a class
      • Created (or “instantiated”) by client code – You create an object by using the new operator
      • Each object is uniquely referenced by its memory address (no need for primary keys, as in a database)
    • Object management
      • Objects are allocated on the garbage-collected heap
      • An object remains allocated until the last remaining object reference disappears
      • At this point, the object is available for garbage collection
      • The garbage collector will reclaim its memory sometime thereafter
      • The garbage collector runs periodically, in a non-deterministic kind of way, to free up the memory used by unreferenced objects.
  3. OO Modelling
    • During OO analysis and design, you map the real world into candidate classes in your application
    • The Unified Modelling Language is a standard and well-respected notation for expressing object oriented analysis and design decisions. There are several types of diagram you can create:
      • Use-case diagrams are useful during analysis. You can identify what kinds of users will use the system, and what functionality they need from the system.
      • Class diagrams capture information about what classes will exist in the system, and how they relate to each other.
      • Sequence diagrams show how objects will interact with each other dynamically when the application is running.
      • Object diagrams are similar to class diagrams, except they show object instances rather than classes. For example, an object diagram might show two DatabaseManager objects and describe how they represent a primary data store and a secondary data store in the application.
      • State diagrams are useful if a class’s behaviour is highly dependent on its current state. A state diagram models the different states an object can be in, and identifies the operations allowed on an object in each of these states.
    • UML is the standard OO notation
      • Widely used
      • Degree of ceremony varies from one organization to another
      • For more information about UML, see https://www.uml.org/

Lab 2: Defining and using a class

Lab 2: Defining and using a class
  1. General Syntax for Class Declarations
    • General syntax for declaring a class:
      • Class names tend to start with a capital letter in Python, to differentiate class names from other identifiers such as functions and variables.
      • The body of a class in Python is a block. It starts with a colon and the attributes are indented (similar to indentation in an if-statement or a loop).
        class ClassName :
         #
         # Define attributes (data and methods) here.
         #
    • Example:
      • Define a class named BankAccount
      • We’ve put this class in a module named accounting.py, in expectation that we might want to define other accounting-related classes in the same module some time in the future.
  2. Creating Objects
    • To create an instance (object) of the class:
      • Use the name of the class, followed by parentheses
      • Pass initialization parameters if necessary (see later)
      • You get back an object reference, which points to the object in memory
        objectRef = ClassType(initializationParams)
    • Example
      • Python returns a reference to the new object. Typically, you store this reference in a variable, to allow you to access the object you’ve just created.
      • Note that Python doesn’t have a new keyword, unlike C++, Java, JavaScript, C#, etc. Watch out for this if you’ve used one of these other languages.
        from accounting import BankAccount
        acc1 = BankAccount()
        acc2 = BankAccount()
  3. Defining and Calling Methods
    • In OO terminology, a method is a function defined inside a class. The purpose of a method is to perform some tasks upon an instance of that class
    • Consider the example below:
      • The purpose of the deposit() method is to deposit an amount of money into a particular BankAccount object
      • The purpose of the withdraw() method is to withdraw an amount of money from a particular BankAccount object.
    • In Python, methods in a class must be defined with an “extra” first parameter that identifies the target object.
      • By convention, Python developers name this parameter self. It’s similar to the concept of the this reference in Java or C#, or the this pointer in C++ and allows the method to access attributes in the target object
      • What makes Python different from these other languages is that you have to explicitly declare this parameter at the start of all your methods.
        class BankAccount :
         def deposit(self, amount):
          print("TODO: implement deposit() code")
         def withdraw(self, amount):
          print("TODO: implement withdraw() code")
      • View Code File

    • Client code can call methods on an object
      acc1 = BankAccount()
      acc1.deposit(200)
      acc1.withdraw(50)
    • In order for client code to invoke deposit() or withdraw(), it must specify the target BankAccount object (e.g. acc1), followed by a dot, followed by the name of the method
      • Inside the parentheses, you pass all the parameters required by the method, except the first one (i.e. you don’t explicitly pass the BankAccount object into the method – Python will do this for you automatically).
  4. Initialization Methods
    • The previous part showed how to define some methods in a class. The next obvious step would be to define some data members as well – e.g. a bank account should have data members such as the bank account id, the name of the account holder, the current balance, etc…
    • If you’re familiar with other OO languages such as Java, C#, C++, etc., you might expect Python would allow you to define data members as follows:
        class BankAccount:
         balance = 0
         name = ""
         ...
    • But this is NOT how Python does it at all. Instead, what you do is to implement a special method named __init__(), and create variables there instead
      • Called automatically by Python, whenever a new object is created
      • The ideal place for you to initialize the new object!
      • Similar to constructors in other OO languages
    • Typical approach:
      • Define an __init__() method, with parameters if needed
      • Inside the method, set attribute values on the target object
      • Perform any additional initialization tasks, if needed
    • Client code:
      • Pass in initialization values when you create an object
    • Here’s an example of how to implement __init__()
      class BankAccount:
        def __init__(self, accountHolder="Anonymous"):
          self.accountHolder = accountHolder
          self.balance = 0.0
        ...
    • View Code File

    • This is how client code creates objects now
      acc1 = BankAccount("Fred")
      acc2 = BankAccount("Wilma")
    • Explanation of the code above:
      • The upper code box shows how to implement the __init__() method for the BankAccount class. The method receives a self parameter (or whatever you want to call it), to identify the target object to be initialized. The method uses the following syntax to add an attribute named accountHolder to the target object:
        self.accountHolder = accountHolder
      • This is the way that you define instance variables in Python, i.e. you add attributes to the target object dynamically at run time, during the object’s initialization.
      • The lower code box shows how client code can create BankAccount objects now. For each object, we specify the name of the account holder. This parameter value is passed automatically into the __init__() method.
  5. Making an Object’s Attributes Private
    • One of the goals of OO is encapsulation
      • Keep things as private as possible
    • Object attributes in Python are public by default. This is quite a shock to C++ / Java / C# programmers! It means client code has unhindered access to the attributes of an object.
      acc1 = BankAccount("Fred")
      print("acc1 account holder is %s" % acc1.accountHolder)
    • The code box above shows the effect of this. The client code can access attributes on the BankAccount object directly, e.g. accountHolder or balance.
      • Client code definitely shouldn’t be allowed to access the attributes on an object directly. If you allowed this to happen, the client programmer would need to know about all the internal variables in a class, to make sure that all the variables remain consistent with each other whenever you change anything.
    • To make an object’s attributes private:
      • Prefix the attribute name with two underscores, __
        class BankAccount:
          def __init__(self, accountHolder="Anonymous"):
            self.accountHolder = accountHolder
            self.__balance = 0.0
          ...
      • View Code File

    • If you did want to allow client code to get the balance, you could add a getBalance() method or similar to the BankAccount class.
  6. Implementing Method Behaviour:
    • Here’s a more complete implementation of our class
      class BankAccount:
        """Simple BankAccount class"""
        def __init__(self, accountHolder="Anonymous"):
          self.accountHolder = accountHolder
          self.__balance = 0.0
        def deposit(self, amount):
          self.__balance += amount
          return self.__balance
        def withdraw(self, amount):
          self.__balance -= amount
          return self.__balance
        def toString(self):
          return "{0}, {1}".format(self.accountHolder, self.__balance)
    • View Code File

    • Note the following points:
      • The “”” string at the start of the class is a class documentation string, which can be used to generate documentation for the class. You can use the same syntax to define documentation strings at the start of methods.
      • The deposit() and withdraw() methods update the account’s balance, and return the new balance.
      • The toString() method returns a textual representation of the object, which can be useful for display and diagnostic purposes.
Lab
  1. Defining a class and creating objects
    • Write a Python program that defines a simple Employee class and creates some instances
    • Suggestions and requirements:
      • The Employee class needs to hold the name and salary of the employee, and the date/time he/she joined the company. For the date/time, you can use the datetime class from the datetime module:
        from datetime import datetime
        ...
        currentDateAndTimeVariable = datetime.now()
      • The class must honour the OO principle of encapsulation, so make sure the instance variables are private
      • Create the class and the initialization function for the when we create an object
        from datetime import datetime
        class Employee:
          # Initialization.
          def __init__(self, name, salary):
            self.__name = name
            self.__salary = salary
            self.__joined = datetime.now()
        ...
      • View Code File

      • The class needs to allow an employee to have a pay raise, so define a payRaise() method that takes the amount of the pay raise and adds it to the employee’s current salary:
        # Business methods.
        def payRaise(self, amount):
          self.__salary += amount
      • The class should also have a toString() method that returns a textual representation of the employee’s info. Note, to format the “date joined” value, you can call the strftime(“%c”) method defined in the datetime class:
        def toString(self):
          return "[{0}] {1} earns {2}, joined {3}".format(self.__id, self.__name, self.__salary, self.__joined.strftime("%c"))
      • View Code File

      • Write some client code, where you can create some Employee objects and invoke methods upon them:
        from company import Employee
        # Create employee and print that employee
        emp1 = Employee("Siv", 7000)
        print(emp1.toString())
      • View Code File

Lab 3: Class-wide members

Lab 3: Class-wide members
  1. Class-Wide Variables
    • Class-wide variables belong to the class as a whole
      • Allocated once, before usage of first object
      • Remain allocated regardless of number of objects
    • Common uses of class-wide variables are as follows:
      • To define a “global” counter for a class, e.g. number of instances.
      • To define class-wide values, e.g. the interest rate for all bank accounts.
      • To define class-wide constants, e.g. MILES_TO_KM and KM_TO_MILES.
    • To define a class-wide variable:
      • Define the variable at global level in the class
        class BankAccount:
          __nextId = 1
          __OVERDRAFT_LIMIT = -1000
          ...
      • View Code File

    • In the example above, we’ve decided to define two class-wide variables for the BankAccount class:
      • A class-wide variable that will be incremented every time a new bank account is created. We can use this variable like a database primary key seed, to ensure each new account is assigned a unique id.
      • A class-wide variable to hold the overdraft limit that will apply to all bank accounts.
    • To access the class-wide variable in methods:
      • Prefix with the class name
        def __init__(self, accountHolder="Anonymous"):
          self.accountHolder = accountHolder
          self.__balance = 0.0
          self.id = BankAccount.__nextId
          BankAccount.__nextId += 1
      • View Code File

    • You can decide whether you want to allow client code to access class-wide variables, as follows:
      • If you want to prevent client code from accessing a class-wide variable, then prefix the variable name with __. For example, __nextId and __OVERDRAFT_LIMIT are private to the class. Client code can’t see these variables.
      • If you want to allow client code to access a class-wide variable, then don’t prefix the variable name with __.
    • Here’s an example that puts it all together
      class BankAccount:
        __nextId = 1
        __OVERDRAFT_LIMIT = -1000
        def __init__(self, accountHolder="Anonymous"):
          self.accountHolder = accountHolder
          self.__balance = 0.0
          self.id = BankAccount.__nextId
          BankAccount.__nextId += 1
         def withdraw(self, amount):
          newBalance = self.__balance - amount
          if newBalance < BankAccount.__OVERDRAFT_LIMIT:       print("Insufficient funds to withdraw %f" % amount)     else:       self.__balance = newBalance     return self.__balance   ...
    • View Code File

    • This example shows how to use the __nextId and __OVERDRAFT_LIMIT class variables in our BankAccount class. Note that you must prefix the variable names with the class name, so Python knows which scope it's looking at.
  2. Class-Wide Methods
    • Common uses of class-wide methods are as follows:
      • To define getters and setters for class-wide variables.
      • To implement a factory mechanism, in order to control how instances of the class are created. For example, define class-wide method(s) that create new objects in a controlled way.
      • To house stateless operations that would otherwise be global methods.
    • Class-wide methods don't receive a self parameter, so they have no direct access to any particular instance. As a consequence, class-wide methods can only directly access other class-wide members, which are shared across the entire class.
    • Example:
      class BankAccount:
        __nextId = 1
        __OVERDRAFT_LIMIT = -1000
        ...
        def getOverdraftLimit():
          return BankAccount.__OVERDRAFT_LIMIT
    • View Code File

    • To call a class-wide method, use the name of the class (rather than using a particular object). For example, the code box below shows how to call the getOverdraftLimit() method defined in the BankAccount class.
    • Client code:
      print("Overdraft limit for all accounts is %d" % BankAccount.getOverdraftLimit())
Lab
  1. Implementing "pay bonus" functionality
    • In the Employee class, add a method named payBonus(). The method should be flexible enough to be called in three different ways (you'll need to declare some default argument values to support this behavior):
      • The client can call the method with no parameters. In this case, add a fixed percentage of the employee's salary (e.g. a 1% bonus)
      • The client can call the method with a single parameter, specifying the percentage of the bonus. For example, the client code might request a 10% pay bonus
      • The client can call the method with parameters specifying the percentage of the bonus, along with a minimum and maximum salary (such that the bonus only applies if the employee's salary is within that range)
        def payBonus(self, percentBonus=1, min=None, max=None):
          if (min is None or self.__salary >= min) and \
            (max is None or self.__salary <= max):     self.__salary *= 1 + percentBonus / 100 
    • You can test the functionality using this client code
      from company import Employee
      # Create employee, and give a default bonus (1%).
      emp1 = Employee("Siv", 7000)
      emp1.payBonus();
      print(emp1.toString())
      # Create employee, and give a 10% bonus.
      emp2 = Employee("Joe", 15000)
      emp2.payBonus(10);
      print(emp2.toString())
    • View Code File

  2. Adding class-wide members
    • Refactor your Employee class to make appropriate use of class-wide variables and methods
    • Suggestions and requirements:
      • In the Employee class, define a class-wide variable to hold the statutory minimum salary for all employees. Set it to 12000. Use this class variable in the constructor, to ensure the employee earns at least this much
        ################### Class variables.
        __Salary = 12000
        __nextEmployeeID = 0
        ######################### Initialization.
        self.__salary = max(salary, Employee.__minimumSalary)
      • View Code File

      • Define class-wide methods to get / set the statutory minimum salary. Call these methods from your test code.
        ########################## Class methods.
        def getMinimumSalary():
          return __minimumSalary
        def setMinimumSalary(s):
          __minimumSalary = s
        ################################ client code
        # Increase the minimum salary.
        Employee.setMinimumSalary(18000)
        # Create employee, and give a 10% bonus if salary between 10000 and 20000.
        emp3 = Employee("Adi", 15000)
        emp3.payBonus(10, 10000, 20000)
        print(emp3.toString())
        # Create employee, and give a 10% bonus if salary between 50000 and 80000.
        emp4 = Employee("Ole", 15000)
        emp4.payBonus(10, 50000, 80000)
        print(emp4.toString())
      • View Code File
        View Code File

 

Well done. You have completed the tutorial in the Python course. The next tutorial is

7. Additional Object-Oriented Techniques


Back to beginning
Copyright © 2016 TalkIT®






If you liked this post, please comment with your suggestions to help others.
If you would like to see more content like this in the future, please fill-in our quick survey.
Scroll to Top