How do I create a private constructor which should be called only by the static function of the class and not from else where?
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.
OnlyCreatable._OnlyCreatable__create_key
. Or you can run Python with assertions disabled using the -O
flag or PYTHONOPTIMIZE
env var. –
Towelling __init__
, make it an if-statement instead of an assertion -- assertions can easily be disabled and in some environments they are. Also, you may replace ==
test for equality with is
, to make programmer's intention clear if not for anything else. –
Caucasian class Hack: def __eq__(self, other): return True
. Use is
if you want this to work even for evil objects –
Corporative 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!
__new__
, the new operator. However, it is not as powerfull as __new__
in Python. –
Penner 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 method
def factory():
class Foo:
pass
return Foo()
OR
#Assign the class as an attribute of the factory method
def factory():
return factory.Foo()
class Foo:
pass
factory.Foo = Foo
del 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 function
class Foo:
pass
def factory_maker():
inner_Foo=Foo
def factory():
return inner_Foo()
return factory
factory = factory_maker()
del Foo
del 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.
isinstance
checks will be impossible because the class is not accessible (or at least, not supposed to be accessed). That can be solved by letting the class inherit from a visible abstract base class. –
Digitize Though strictly private attributes do not exist in Python, you can use a metaclass to prevent the use of the MyClass()
syntax to create a MyClass
object.
Here is an example adapted from the Trio project:
from typing import Type, Any, TypeVar
T = TypeVar("T")
class NoPublicConstructor(type):
"""Metaclass that ensures a private constructor
If a class uses this metaclass like this:
class SomeClass(metaclass=NoPublicConstructor):
pass
If you try to instantiate your class (`SomeClass()`),
a `TypeError` will be thrown.
"""
def __call__(cls, *args, **kwargs):
raise TypeError(
f"{cls.__module__}.{cls.__qualname__} has no public constructor"
)
def _create(cls: Type[T], *args: Any, **kwargs: Any) -> T:
return super().__call__(*args, **kwargs) # type: ignore
Here is an example of use:
from math import cos, sin
class Point(metaclass=NoPublicConstructor):
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def from_cartesian(cls, x, y):
return cls._create(x, y)
@classmethod
def from_polar(cls, rho, phi):
return cls._create(rho * cos(phi), rho * sin(phi))
Point(1, 2) # raises a type error
Point.from_cartesian(1, 2) # OK
Point.from_polar(1, 2) # OK
If a module-level function is OK instead of a static method ...
Make the whole class private, expose the API as an abstract class, and use a function to instantiate your private class
class Foo(ABC):
@abstractmethod
def bar(self) -> int:
...
class _SpecificFoo(Foo):
def __init__(self, bar: int):
self._bar = bar
def bar(self) -> int:
return self._bar
def specific_foo(bar: int) -> Foo:
return _SpecificFoo(bar)
Note
_SpecificFoo()
andspecific_foo()
can have different signaturesspecific_foo()
returns aFoo
. The API makes no mention of_SpecificFoo
This is very similar to Jesse's first option, but the class isn't redefined on each call to specific_foo
, and the class interface is statically typed.
Foo
in type annotations, but the protected status of _SpecificFoo
will strongly encourage us to instatiate new instances using specific_foo
instead. This is also more pythonic than solutions that actually throw an error if we try to use the constructor. It's better to just discourage it using an underscore. I'm surprised this doesn't have more up votes. –
Nine Quoting the Python style guide (PEP 8):
In addition, the following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):
_single_leading_underscore
: weak "internal use" indicator. E.g. "from M import *
" does not import objects whose name starts with an underscore.
single_trailing_underscore_
: used by convention to avoid conflicts with Python keyword, e.g.Tkinter.Toplevel(master, class_='ClassName')
__double_leading_underscore
: when naming a class attribute, invokes name mangling (inside class FooBar,__boo
becomes_FooBar__boo
; see below).
__double_leading_and_trailing_underscore__
: "magic" objects or attributes that live in user-controlled namespaces. E.g.__init__
,__import__
or__file__
. Never invent such names; only use them as documented.
Fist of all, the term "constructor" does not apply to Python, because, although __init__()
method plays a role of one, it is just a method which is called when an object has already been created and requires initialization.
Every method of a class in Python is public. Generally programmers mark "private" methods with _
or __
in the name of a method, e.g.:
# inheriting from object is relevant for Python 2.x only
class MyClass(object):
# kinda "constructor"
def __init__(self):
pass
# here is a "private" method
def _some_method(self):
pass
# ... and a public one
def another_method(self):
pass
MyClass
with __my()
method. Consider mc_obj = MyClass()
. You'll be able to access __my()
via mc_obj._MyClass__my()
. –
Noleta eval()
or of using assignments such as vars()['a'] = 10
instead of a = 10
. –
Gadabout __
methods when I want the functionality they offer. What I wanted to point out is that beginning method's name with _
is not the same as __
and if you did not notice it already, see @gimel's or @mac's answers, please. –
Gadabout If a module-level function is OK instead of a static method, see my other answer
You can achieve something to this effect with an abstract class. Any instance attributes that need defining in the "private constructor" can be abstract properties. Your factory class method then builds its own concrete class by populating these abstract attributes, as well as doing any other initialisation work such as data validation.
from abc import ABC, abstractmethod
class Foo(ABC):
@property
@abstractmethod
def _a(self) -> int:
pass
def bar(self) -> int:
return self._a + 1
@classmethod
def from_values(cls, a: int) -> 'Foo':
class _Foo(cls):
def __init__(self, __a):
self.__a = __a
@property
def _a(self):
return self.__a
return _Foo(a)
Foo() # TypeError: Can't instantiate abstract class ...
Foo.from_values(1).bar() # 1
If you find you need no abstract attributes on Foo
, you won't get the TypeError
when calling Foo()
. In that case you can either rely on the inheritance from ABC
as documentation, or define a dummy attribute for safety.
Optional tweaks
- Need mutable instance attributes? Add setters.
- Don't care about the difference between class and instance attributes? Simplify with
class _Foo(cls): _a = a return _Foo()
If you just want a simple way to signal that a constructor isn't supposed to be used directly, you can follow the following pattern to make the default usage of the constructor raise an exception:
class Foo:
def __init__(self, arg, __private=False):
assert __private, "Constructor shouldn't be called directly"
self.arg = arg
@classmethod
def from_bar(cls, bar: Bar):
return cls(bar.arg, __private=True)
@classmethod
def from_baz(cls, baz: Baz):
return cls(baz.arg, __private=True)
Of course, anyone can still do Foo('abc', __private=True)
but it would send a clear signal that such behavior isn't supported which follows the general design of Python.
PYTHONOPTIMIZE=TRUE
) Python will skip checking asserts. If this is a case where you still want this check, you may consider changing assert
to if not __private: raise RuntimeError("...")
. –
Roussillon __private
can be made even more obscure(but possibly less portable) by using unicode characters. –
Wrecker Other answers have mentioned that there is no such thing as "private" constructors (or access modifiers in general) in Python. While this is true, you can still take advantage of the __
prefixing behavior to accomplish the same end effect by "hiding" the actual implementation in a nested class with a double-underscore prefix:
class MyPublicClassWithPrivateConstructor:
class __Implementation:
def __init__(self, *args, **kwargs):
# do the things
self.args = args
self.kwargs = kwargs
@classmethod
def make_my_thing(cls, *args, **kwargs)
return cls.__Implementation(*args, **kwargs)
def __new__(cls, *args, **kwargs):
raise Exception("MyPublicClassWithPrivateConstructor cannot be initialized directly, use MyPublicClassWithPrivateConstructor.make_my_thing")
# elsewhere
from whateva import MyPublicClassWithPrivateConstructor
MyPublicClassWithPrivateConstructor.__Implementation # type object 'MyPublicClassWithPrivateConstructor' has no attribute '__Implementation'
MyPublicClassWithPrivateConstructor.make_my_thing # <bound method MyPublicClassWithPrivateConstructor.make_my_thing of <class 'whateva. MyPublicClassWithPrivateConstructor'>>
I know, I'm late but we can do something like this to create private constructor.
class MyClass:
__instance = None
__private = False
@staticmethod
def get_instance():
MyClass.__private = True
__instance = MyClass()
MyClass.__private = False
return MyClass.__instance
def __init__(self):
if MyClass.__private == False:
raise Exception("Its a private method. Please use get_instance method")
if not MyClass.__instance:
condition and put everything outside of condition, and that will work. Let me edit it for general use cases. –
Indogermanic MyClass()
while another thread has set __private
to True
–
Corporative Another solution, is to let __init__
ask for a kind of private secret that only factory methods will know.
See example below:
from __future__ import annotations
class Foo:
name: str
def __init__(self, check_private_constructor: str):
if check_private_constructor != "from_within":
raise RuntimeError("Please use named constructors!")
@staticmethod
def from_name(name: str) -> Foo:
r = Foo("from_within")
r.name = name
return r
@staticmethod
def from_index(index: int) -> Foo:
r = Foo("from_within")
r.name = str(index)
return r
def __str__(self):
return "Foo.name=" + self.name
# a = Foo("eee") # will raise an exception
a = Foo.from_name("Bob")
print(a)
b = Foo.from_index(3)
print(b)
A clear and lightweight solution is to make the constructor arguments required but "private". This only really works if you also make them keyword-only, else it's too easy to miss
class Foo:
def __init__(self, *, _x: int):
self._x = _x
@classmethod
def create(cls, x: int) -> Foo:
return Foo(_x=x)
Here, Foo(_x=1)
indicates we're misusing the API.
It's debatable whether Foo(_x=x)
in create
is breaking the class's own contract. I'm undecided.
class MongoConn(object):
@classmethod
def instance(cls):
if not hasattr(cls, '_instance'):
cls._instance = cls()
return cls._instance
def __init__(self):
assert not hasattr(self.__class__, '_instance'), 'Do not call constructor directly!'
If you want a single instance.
MongoConn()
here no problem –
Corporative © 2022 - 2024 — McMap. All rights reserved.
__
can still be called asobj._ClassName__Method()
. Now would you please explain, how this is private? – Noletaobj._ClassName__Method()
outside ofClassName
is correct behaviour. – Gadabout