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 fromRealExtensions
) 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()
- Compute the magnitude of the complex number
- A subclass called
Dual
(inherits fromRealExtensions
) 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()
- Compute the magnitude of the dual number
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:¶
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)
Part 2¶
Once you're happy with your module, make the following change:
- Rename the
Complex
subclass as_Complex
. Save the new module asmynumbers_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.
- Note: You can use
Demo your code in the cell below.
from mynumbers_p import *
zc = Complex(2.0, 1.0)
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:
import mynumbers_m as myn_m
z = myn_m.Complex(1,1)
z.number()
print(z._RealExtensions__number, z._Complex__number)
vars(z)
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).