Private Constructor in Python Private Constructor in Python python python

Private Constructor in Python


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-methodcreation 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.


How do I create a private constructor?

In essence, it's impossible both because python does not use constructors the way you may think it does if you come from other OOP languages and because python does not enforce privacy, it just has a specific syntax to suggest that a given method/property should be considered as private. Let me elaborate...

First: the closest to a constructor that you can find in python is the __new__ method but this is very very seldom used (you normally use __init__, which modify the just created object (in fact it already has self as first parameter).

Regardless, python is based on the assumption everybody is a consenting adult, thus private/public is not enforced as some other language do.

As mentioned by some other responder, methods that are meant to be "private" are normally prepended by either one or two underscores: _private or __private. The difference between the two is that the latter will scramble the name of the method, so you will be unable to call it from outside the object instantiation, while the former doesn't.

So for example if your class A defines both _private(self) and __private(self):

>>> a = A()>>> a._private()   # will work>>> a.__private()  # will raise an exception

You normally want to use the single underscore, as - especially for unit testing - having double underscores can make things very tricky....

HTH!


As no-one has mentioned this yet -- you can have considerable control over what names are visible in what scopes -- and there are lots of scopes available. Here are two three other ways to limit construction of a class to a factory method:

#Define the class within the factory methoddef factory():  class Foo:    pass  return Foo()

OR

#Assign the class as an attribute of the factory methoddef factory():  return factory.Foo()class Foo:  passfactory.Foo = Foodel Foo

(Note: This still allows the class to be referred to from outside (for isinstance checks, for example), but it makes it pretty obvious that you aren't supposed to instantiate it directly.)

OR

#Assign the class to a local variable of an outer functionclass Foo:  passdef factory_maker():  inner_Foo=Foo  def factory():    return inner_Foo()  return factoryfactory = factory_maker()del Foodel factory_maker

This makes it impossible (at least, without using at least one magic (double underscore) property) to access the Foo class, but still allows multiple functions to use it (by defining them before deleting the global Foo name.