Restrict the creation of objects in python

While c++ and java has authorization system for accessing class entities such as methods and member variables, Python does not have one. In this article, we will see how we can we prevent accidents by creating authorization system which allows to restrict the creation of objects in python.

The Problem

Suppose we want to model a simple world – A Manager and Worker. The Manager has two rules – he hire and fire a Worker. The following restriction should be enforced – Only the Manager can create a Worker object while he hire someone.

We start to code this simple world:

Sample #1
 class Worker:
     def __init__(self,name):
         self.name = name
     def __str__(self):
         return 'Worker <{0}>'.format(self.name)

 class Manager:
     def __init__(self,name):
         self.name = name
         self.workers = []
     def __str__(self):
         return 'Manager <{0}> | {1}'.format(
             self.name,
             ','.join( [str(worker) for worker in self.workers] )
         )

     def hire(self, name):
         worker = Worker(name)
         self.workers.append(worker)
         return worker

     def fire(self, name):
         ii = 0
         while ii < len(self.workers):
             if self.workers[ii].name == name:
                 self.workers.pop(ii)
             else:
                 ii += 1

We write a simple test for our code:

Sample #1 - test
 import world
 manager = world.Manager('A') ; assert str(manager) == "Manager <A> | "
 manager.hire('1')            ; assert str(manager) == "Manager <A> | Worker <1>"
 manager.hire('2')            ; assert str(manager) == "Manager <A> | Worker <1>,Worker <2>"
 manager.fire('1')            ; assert str(manager) == "Manager <A> | Worker <2>"

 print( "I can create a worker : {0}".format(str(world.Worker('1')) ) )

When we run the test it seems to work as expected. However, the output shows that the programmer which use our world can create the class Worker.

Sample #1 - test output
 $ python test.py
 I can create a worker : Worker <1>

Restrict the Creation of Objects – Step 1

Python does not have a builtin authorization system for accessing entities. So, how can we control who can construct the Worker object?

We will create a simple authorization system. Please note that the main purpose of this authorization system is to avoid accidents – If the programmer wants to bypass this authorization system, he will be able to do it with a little effort.

First, we will use pseudo private variables which is based on the good will of programmer to follow the convection and it is designed mostly to avoid accidents. The current mechanism is name mangling. Any identifier of the from __BB in a class is textually replaced with _classname__BB.

We will define the Worker class in Manager and make it 'private' by adding '__' at the beginning of the name:

Sample #2 - pseudo private variables
 class Manager:
     class __Worker:
         ...

     def __init__(self,name):
         ...
     def __str__(self):
         ...

     def hire(self, name):
         worker = Manager.__Worker(name)
         self.workers.append(worker)
         return worker

     def fire(self, name):
         ...

We will also change the test to reflect the name change:

Sample #2 - test
 ...
 print( "I can create a worker : {0}".format(str(world.Manager.__Worker('1')) ) )

Now, when running the test, an exception is raised:

Sample #2 - test output
 $ python test.py
 Traceback (most recent call last):
     File "test.py", line 7, in <module>
     print( "I can create a worker : {0}".format(str(world.Manager.__Worker('1')) ) )
 AttributeError: type object 'Manager' has no attribute '__Worker'

As said, the programmer can still create the class if he wants to and know about the name mangling mechanism, as the following test shows:

Sample #2 - test #2
 import world
 print( "I can create a worker : {0}".format(str(world.Manager._Manager__Worker('1')) ) )
Sample #2 - test #2 output
 $ python test2.py
 I can create a worker : Worker <1>

In the next step we will see how can we improve the authorization system with fetching the function callee