Private Constructor in Python

user1057793 picture user1057793 · Nov 21, 2011 · Viewed 27.4k times · Source

How do I create a private constructor which should be called only by the static function of the class and not from else where?

Answer

Adam picture Adam · Sep 28, 2017

The _ and __ prefixes don't offer a solution to restricting instantiation of an object to a specific 'factory', however Python is a powerful toolbox and the desired behaviour can be achieved in more than one way (as @Jesse W at Z has demonstrated). Here is a possible solution that keeps the class publicly visible (allowing isinstance etc.) but ensures construction is only possible by class-methods:

class OnlyCreatable(object):

    __create_key = object()

    @classmethod
    def create(cls, value):
        return OnlyCreatable(cls.__create_key, value)

    def __init__(self, create_key, value):
        assert(create_key == OnlyCreatable.__create_key), \
            "OnlyCreatable objects must be created using OnlyCreatable.create"
        self.value = value

Constructing an object with the create class-method:

>>> OnlyCreatable.create("I'm a test") 
<__main__.OnlyCreatable object at 0x1023a6f60>

When attempting to construct an object without using the create class-method creation fails due to the assertion:

>>> OnlyCreatable(0, "I'm a test")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in __init__
AssertionError: OnlyCreatable objects can only be created using OnlyCreatable.create

If attempting to create an object by mimicking the create class-method creation fails due to compiler mangling of OnlyCreatable.__createKey.

>>> OnlyCreatable(OnlyCreatable.__createKey, "I'm a test")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'OnlyCreatable' has no attribute '__createKey'

The only way to construct OnlyCreatable outside of a class-method is to know the value of OnlyCreatable.__create_key. Since this class-attribute's value is generated at runtime and it's name is prefixed with __ marking it as inaccessible it is effectively 'impossible' to obtain this value and/or construct the object.