Key Word(s): Privacy, Name mangling, Modules, vars



Exercise 1: "Private" Methods and Names in Python

Background

We will consider two extensions to the real numbers: complex numbers and dual numbers.

Complex Numbers

A complex number is defined as $$z = a + ib$$ where $i^{2} = -1$. $a$ is the real part and $b$ is the imaginary part. The polar form of a complex number is $$z = \left|z\right|e^{i\theta}$$ where $\left|z\right|^{2} = zz^{*} = a^{2} + b^{2}$ and $z^{*} = a - ib$ is the complex conjugate of $z$. The angle between $a$ and $b$ is given by $$\theta = \tan^{-1}\left(\frac{b}{a}\right).$$

Dual Numbers

The dual numbers look similar. We have $$d = a + \epsilon b$$ where $\epsilon$ is a number (not zero!) such that $\epsilon^{2} = 0$. Once again $a$ is the real part, but here $b$ is the dual part. The polar form of this number is $$d = \displaystyle a\left(1 + \epsilon \frac{b}{a}\right).$$ Note that the magnitude of the dual number $\left|d\right| = a$ since $dd^{*} = \left(a + \epsilon b\right)\left(a - \epsilon b\right) = a$ where $d^{*} = a - \epsilon b$ is the conjugate of $d$. Finally, the angular part is $$m = \dfrac{b}{a}.$$

Dual numbers are a route to automatic differentiation. We'll mention them again in the AD lectures.

Problem Description

Part 1

For today, your task is to write a module called mynumbers.py. The module should contain at a minimum the following:

  • A base class called RealExtensions with a constructor that accepts $a$ and $b$.
  • A subclass called Complex (inherits from RealExtensions) that has the following methods:
    • Compute the magnitude of the complex number
      • Note: This method should be preceeded by a single underscore to indicate that it should not be accessed by a user.
    • Compute the "angle" of the complex number
      • Note: This method should be preceeded by a single underscore to indicate that it should not be accessed by a user.
    • Compute the polar form of the complex number using _magnitude() and _angle()
  • A subclass called Dual (inherits from RealExtensions) that has methods for computing the magnitude and "angle" of the dual number.
    • Compute the magnitude of the dual number
      • Note: This method should be preceeded by a single underscore to indicate that it should not be accessed by a user.
    • Compute the "angle" of the dual number
      • Note: This method should be preceeded by a single underscore to indicate that it should not be accessed by a user.
    • Compute the polar form of the dual number using _magnitude() and _angle()

The choice of "hiding" the magnitude() and angle() methods may or may not be a good one. The goal is to show that this is not really privacy; it's more of a contract between people using the code. Note that a user can still access _magnitude() and _angle(). However, this is a rather strong contract among Python programmers and is used widely in Python code.

Import your module using import mynumbers and play around with creating complex and dual numbers. Demo your code in the cell below:

In [1]:
import mynumbers as myn

zc = myn.Complex(2.0,1.0) # complex number
print(zc.real, zc.imag)

zd = myn.Dual(2.0,2.0)
print(zd.real, zd.dual)

zc_polar = zc.polar_form()
print(zc_polar)

zd_polar = zd.polar_form()
print(zd_polar)
2.0 1.0
2.0 2.0
(2.23606797749979, 0.46364760900080615)
(2.0, 1.0)

Part 2

Once you're happy with your module, make the following change:

  • Rename the Complex subclass as _Complex. Save the new module as mynumbers_p.
  • Now import your module using from mynumbers_p import *
  • Try to create a complex number. What happens?
    • Note: You can use dir() to see what's in your namespace.

Demo your code in the cell below.

In [2]:
from mynumbers_p import *

zc = Complex(2.0, 1.0)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
 in 
      1 from mynumbers_p import *
      2 
----> 3 zc = Complex(2.0, 1.0)

NameError: name 'Complex' is not defined

Part 3: Name Mangling Mechanics

This part is optional

For some reason you decide to store the components of your number in an immutable data structure. Do this in the base class (RealExtensions) via a tuple attribute called number. Provide a method called number in the Complex subclass which stores the number as a list in an attribute named number. If the user calls the number method on a Complex instance, then you automatically store the number in an immutable data structure as specified in the base class constructor. Use super() to call the base class constructor. Meanwhile, still in the number method of the Complex subclass, store the mutable list of numbers as an attribute in the subclass. Once again, name this attribute number.

To avoid possible namespace collisions, mangle the names. That is, instead of naming the number methods just number, name them as __number.

Save your module as mynumbers_m.py.

Your module should be importatable and demoed as follows:

In [3]:
import mynumbers_m as myn_m 

z = myn_m.Complex(1,1)
z.number()
print(z._RealExtensions__number, z._Complex__number)
vars(z)
(1, 1) [1, 1]
Out[3]:
{'real': 1,
 'imag': 1,
 'a': 1,
 'b': 1,
 '_RealExtensions__number': (1, 1),
 '_Complex__number': [1, 1]}

Some Comments on Part 3

This exercise was meant to illustrate the mechanics of name mangling. The idea of privacy via name mangling in Python is a bit controversial Ian Bicking at Paste Style Guide

Never, ever use two leading underscores. This is annoyingly private. If name clashes are a concern, use explicit name mangling instead (e.g., _MyThing_blahblah). This is essentially the same thing as double-underscore, only it’s transparent where double underscore obscures.

One potential use case is if a class is meant to be extended many times (see, e.g. Why are Python's 'private' methods not actually private?). This example attempted to highlight this possible use case.

Deliverables

  • mynumbers.py
  • mynumbers_p.py
  • mynumbers_m.py --- Optional
  • A demo notebook saved as lecture9-demo.ipynb
    • The notebook can look exactly like this one (problem statements and all).