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.
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 |
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: | 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
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}' |
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.
Downstream code that uses the car simulator can use an error handler like this in Python:
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.