© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
A. DanialPython for MATLAB Developmenthttps://doi.org/10.1007/978-1-4842-7223-7_10

10. Object-Oriented Programming

Albert Danial1  
(1)
Redondo Beach, CA, USA
 

Python is object oriented to its core. Every item—variable, function, operator, exception, signal, even a string literal—is an object. MATLAB also supports object-oriented programming, in fact with greater rigor than Python, but with less fluidity as well.

Neither language forces one to define classes; conventional procedure–based programming is entirely possible and may even be the default for most scientists and engineers. The ease with which one can define and use classes in Python, however, simplifies the path for developers new to OO to try this programming paradigm.

For the uninitiated, an object is an instance of a class in the same way a variable is an instance of a data type. Before we can define j as an integer, for example, the concept of an integer type must exist. Of course, MATLAB and Python predefine many useful data types and containers. Classes can be thought of as a kind of user-defined container which can include functions, called methods, that operate on the data in that container. Before we can work with objects, we must define classes from which objects are made.

10.1 Classes

Here’s an example of a simple class that defines a circle. It includes three variables, x, y, and r, that define the center and radius and one method, area(), that returns the circle’s area. The variables x, y, and r are known as instance variables because each object created from this class (i.e., each class instance) will have its own values for the center and radius.

In addition to instance variables, we’ll also define a class variable, MAX_R, which is shared by all objects made from our class.

Python classes require an explicit constructor function called __init__() whose first argument is always the placeholder self. One can then create arbitrarily named attributes in the class using dot notation. Most often, this involves assigning the constructor’s calling arguments to class attributes with lines like self.x = x.

MATLAB:

Python:

% code/OO/Circle.m

classdef Circle

  properties (Constant)

     MAX_R = 20;

  end

  properties

     x {mustBeNumeric}

     y {mustBeNumeric}

     r {mustBeNumeric}

  end

  methods

    function obj = Circle(x,y,r)

      obj.x = x;

      obj.y = y;

      obj.r = r;

      obj.perim = 2*r*pi;

    end

    function [A] = area(obj)

      A = pi*obj.r^2;

    end

  end

end

import numpy as np

class Circle:

  MAX_R= 20

  def __init__(self, x,y,r):

    self.x = x

    self.y = y

    self.r = r

    if self.r > circle.MAX_R:

      print('too big')

      self.r = circle.MAX_R

    self.R = r

  def area(self):

    return np.pi*self.r**2

With our Circle class defined, we’ll make objects a and b which are instances of Circle :

MATLAB:

Python:

>> a = Circle(0,0,10);

>> b = Circle(1,2,4);

>> b.y

2

>> a.area()

314.1592

In : a = Circle(0,0,10)

In : b = Circle(1,2,4)

In : b.y

Out: 2

In : a.area()

Out: 314.1592

The act of creating an object from a class, as with a = Circle(0,0,10), causes the class’s constructor method—a function with the same name as the class in MATLAB, and the function __init__() in Python— to be called with the provided arguments.

10.1.1 Private vs. Public

Most object-oriented languages allow tight control over access to a class’s attributes and methods by designating these as public or private. The former can be accessed by other objects, while the latter can only be seen and updated by members of the object itself.

MATLAB has mechanisms to allow access control, but Python is much looser in this regard, relying on naming conventions to indicate the developer’s intent. In a nutshell, Python class attributes prefixed by two underscores are name mangled to hide them as though they were private. Name mangling simply consists of internally prefixing the variable to include the name of the class so a determined user will still be able to reach the data. The following example shows a variation of the Circle class defining an additional attribute, perim, as a private variable storing the circle’s perimeter.

MATLAB:

Python:

% file: code/OO/Circle.m

classdef Circle

  properties (Constant)

     MAX_R = 20;

  end

  properties (Access = private)

     perim

  end

  properties

      x {mustBeNumeric}

      y {mustBeNumeric}

      r {mustBeNumeric}

  end

  methods

   function obj = Circle(x,y,r)

     obj.x = x;

     obj.y = y;

     obj.r = r;

     obj.perim = 2*r*pi;

   end

   function [A] = area(obj)

     A = pi*obj.r^2;

   end

 end

end

# file: code/OO/circle.py

import numpy as np

class Circle:

  MAX_R= 20

  def __init__(self, x,y,r):

    self.x = x

    self.y = y

    self.R = r

    self.__perim = 2*r*np.pi;

  def area(self):

    return np.pi*self.r**2

10.1.2 Custom Printers

The printed representation of a class can be changed in MATLAB by overloading the disp() function and in Python by defining a custom __str__() method. The following functions can be added to the preceding Circle class to print the circle’s properties in a tight format. Note that __str__() returns a string rather than calling a print function directly.

MATLAB:

Python:

function disp(obj)

 fprintf('X: %5.2f Y: %5.2f ', ...

         obj.x, obj.y);

 fprintf('R: %5.2f ', obj.r);

end

def __str__(self):

  return f'X: {self.x:5.2f} '

         f'Y: {self.y:5.2f} '

         f'R: {self.R:5.2f}'

Our custom output then looks like this:

MATLAB:

Python:

>> c = Circle(.4,6.6, 3.14);

>> disp(c)

X:  0.40 Y:  6.60 R:  3.14

In : c = Circle(.4,6.6, 3.14)

In : print(c)

X:  0.40 Y:  6.60 R:  3.14

10.1.3 Custom Exceptions

As a code base grows, so does the number of ways it can fail. Exception handlers, helpful for managing failure modes, can be enhanced with custom exceptions that yield more revealing and application-specific error states than generic errors. Custom exceptions are easily added to both MATLAB and Python. The following example implements a simple electric car simulator that throws a BatteryEmptyError exception when the car’s battery is depleted. The exception is defined with lines 2–4 in MATLAB and 3–4 in Python.

MATLAB:
 1   % code/OO/ecar_sim.m
 2   errID = 'ecar_sim:NotNumeric';
 3   msg = 'BatteryEmptyError';
 4   BatteryEmptyError = MException(errID,msg);
 5   charge = 80.0; % kWhr
 6   d_rate = 0.002;
 7   while true
 8       km   =  50*rand();
 9       kmph = 20 + 120*rand();
10       discharge = d_rate*km*(20 + abs(kmph - 80))^1.4;
11       charge = charge - discharge;
12       if charge < 0
13           throw(BatteryEmptyError)
14       end
15       fprintf(' %4.1f km at %6.2f km/hr remaining: %5.2f kWhr ',...
16             km, kmph, charge);
17   end
Python:
 1   # code/OO/ecar_sim.py
 2   import numpy as np
 3   class BatteryEmptyError(Exception):
 4       pass
 5   charge = 80.0 # kWhr
 6   d_rate = 0.002
 7   while True:
 8       km   =  50*np.random.rand()
 9       kmph = 20 + 120*np.random.rand()
10       discharge = d_rate*km*(20 + np.abs(kmph - 80))**1.4
11       charge -= discharge
12       if charge < 0:
13           raise BatteryEmptyError
14       print(f' {km:4.1f} km at {kmph:6.2f} km/hr '
15             f'remaining: {charge:5.2f} kWhr')
Output from both programs looks similar; Python’s is
 21.0 km at  98.29 km/hr remaining: 73.09 kWhr
  2.9 km at  20.97 km/hr remaining: 70.45 kWhr
 21.8 km at  73.85 km/hr remaining: 66.25 kWhr
 10.9 km at  76.34 km/hr remaining: 64.43 kWhr
  5.7 km at 124.71 km/hr remaining: 60.50 kWhr
 40.9 km at  21.33 km/hr remaining: 23.66 kWhr
 Traceback (most recent call last):
File "code/OO/ecar_sim.py", line 13, in <module> raise BatteryEmptyError
        BatteryEmptyError

Downstream code that uses the car simulator can use an error handler like this in Python:

Python:
try:
    run_ecar_sim()
except BatteryEmptyError:
    print(f'Ran out of juice, recharge')
    bring_service_truck()

10.2 Performance Implications

Object-oriented programming can inhibit computational performance because it ruins data locality; values are dispersed across objects rather than near each other in tight arrays. This defeats vectorization in both Python and MATLAB.

Before we can demonstrate this however, we’ll first need to cover numerical arrays, vectorization, and computational performance in detail. Chapter 11 covers these in detail. Specifically, Section 11.​1.​17 gives an example of a computational problem solved with and without object-oriented methods. Spoiler: The object-oriented method is much slower.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.87.152