MicroPython : Private Methods

Contents

Introduction

The concept of the private method is a very important topic in object-oriented programming. This discussion is specific to MicroPython. The Python language provides features that aren't supported by the simpler MicroPython version of the language.

All examples in this article are original and have been tested on a micro:bit for correctness using the Mu Editor.

Encapsulation

Encapsulation is the concept of wrapping data and behaviour (methods) into one single unit. In pure OOP terms a class has an exposed public interface with the implementation details hidden.

Providing this advertised interface doesn't change, the internals can readily be altered and improved upon without breaking or requiring changes to any program that uses the class.

As will be discussed, MicroPython does not (unfortunately) enforce the encapsulation found in the true OOP languages such as C++.

Private Methods

The implementation of the class may contain private properties and methods that are not part of the public interface, hence unavailable to a program that is calling or instantiating the class. While other OOP languages implement private class components rigorously, MicroPython doesn't have a formal way to do this.

Python has a way (sort of) to enforce private methods. As this is a series on MicroPython, the Python case will not be discussed here. For those interested in pursuing Python 'private' method naming and the name mangling mechanism there are many good online references available.

MicroPython (and Python) doesn't possess the private or Private keyword common with other languages. Instead, the MicroPython class developer indicates a method should be considered private by prefixing the method name with two underscore (__) characters e.g. __isReady(). Any method so named should not be accessed outside the class.

Note: This is merely a convention and not enforced by the MicroPython interpreter. It is the responsibility of the programmer using the class to respect this convention.

Examples

The first example is a simple one. The FooFighter class has one attribute (name), two 'public' methods (PublicMethod() and CallPrivateMethod()) and one 'private' method (__PrivateMethod()).

The method __PrivateMethod() is called directly by the program thus showing that the MicroPython interpreter does not enforce it as a 'private' method. This is bad programming practice and it is upto the programmer to avoid doing this.

Example 1

# Demonstrates a program calling a class
# method that the designer of the class 
# has signified should be considered
# private. This is poor programming practice
# even though MicroPython allows it.

# Define a class with a constructor,
# two 'public' methods and one
# 'private' method.
class FooFighter:
    # Constructor
    def __init__(self, name):
        self.name = name
    
    def PublicMethod(self):
        return 'PUBLIC METHOD'

    def __PrivateMethod(self):
        return 'PRIVATE METHOD'
    
    def CallPrivateMethod(self):
        return self.__PrivateMethod()

# Create an instance of the FooFighter class.
myFighter = FooFighter('Jane')
print('My fighter is called', myFighter.name)
print()

# Call the public method
print('Call public method:')
print('==> ', myFighter.PublicMethod())
print()

# Call the private method directly.
# This is BAD PRACTICE
print('Call private method directly (BAD):')
print('==> ', myFighter.__PrivateMethod())
print()

# Call the private method via a public method.
# This is GOOD PRACTISE.
print('Call private method ',
       'via a public method (GOOD):')
print('==> ', myFighter.CallPrivateMethod())

Output:

My fighter is called Jane

Call public method:
==>  PUBLIC METHOD

Call private method directly (BAD):
==>  PRIVATE METHOD

Call private method  via a public method (GOOD):
==>  PRIVATE METHOD

The next example provides a safe square root function that won't raise an exception if a non-numeric or negative number is passed to it.

Again, it is shown that direct calls are allowed to 'private' methods, though bad practice.

Example 2

# Program calls 'public' methods (GOOD) and
# 'private' methods (VERY BAD) of a class.

# Square root function.
from math import sqrt

class Math:
    # Private function
    def __SquareRoot(value):
        # Calculates the square root of a
        # given value. The argument is checked
        # that a square root can be calculated.
        # If there is a problem with the argument
        # then -1 is returned.
        if Math.__isOK(value):
            return sqrt(value)
        else:
            return -1

    # Public function
    def SquareRoot(value):
        root = Math.__SquareRoot(value)
        if root != -1:
            print('Square root of', value, '=', root)
        else:
            print("Can't take square root of", value)

    # Private function
    def __isOK(value):
        # Can only take square roots
        # for positive numbers.
        isNum = isinstance(value, (int, float)) 
        if isNum and (value >= 0):
            return True
        else:
            return False

# Calling 'public' method of the Math class.
print("Calling 'public' methods" + 
      "of the Math class (BAD):")
Math.SquareRoot(15)
Math.SquareRoot(-15)
Math.SquareRoot('Spam')
print()

# Calling 'private' methods of the Math class.
# This should NEVER be done if Python programming
# conventions are properly followed.
print("Calling 'private' methods of class Math:")
print('Square root can be calculated for 90?',
       Math.__isOK(9))
print('The square root of 90 =',
       Math.__SquareRoot(90))

Output:

Calling 'public' methods of the Math class:
Square root of 15 = 3.872983
Can't take square root of -15
Can't take square root of Spam

Calling 'private' methods of class Math (BAD):
Square root can be calculated for 90? True
The square root of 90 = 9.486833