Skip to content

Commit b817e78

Browse files
committed
Singleton pattern
0 parents  commit b817e78

File tree

7 files changed

+186
-0
lines changed

7 files changed

+186
-0
lines changed

singleton.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
3+
The Singleton Pattern - In different implementation avatars.
4+
5+
"""
6+
7+
from singletons import singleton_simple
8+
from singletons import singleton_new
9+
from singletons import singleton_call
10+
from singletons import singleton_deco
11+
from singletons import singleton_mp
12+
13+
from multiprocessing import Process
14+
15+
def test_singularity(cls):
16+
""" Test if the given class is a Singleton """
17+
18+
instance1 = cls()
19+
instance2 = cls()
20+
21+
return instance1 == instance2
22+
23+
def f1(x, instance):
24+
instance.x = x
25+
print('instance.x==',instance, instance.x)
26+
27+
def f2(instance):
28+
# Will print modified value in f1
29+
print('instance.x==',instance, instance.x)
30+
31+
def f3(y, instance):
32+
# Add a dynamic attribute y
33+
instance.y = y
34+
35+
def test_multiprocess_singleton(cls):
36+
""" Test multiprocess shared singleton """
37+
38+
s = cls(x=100)
39+
p1 = Process(target=f1, args=(200, s))
40+
p2 = Process(target=f2, args=(s,))
41+
p3 = Process(target=f3, args=('shared memory', s))
42+
43+
p1.start()
44+
p2.start()
45+
p3.start()
46+
47+
p1.join()
48+
p2.join()
49+
p3.join()
50+
51+
# Print final values
52+
print('instance.x=',s.x)
53+
print('instance.y=',s.y)
54+
55+
# Assertions
56+
assert(s.x == 200)
57+
assert(s.y == 'shared memory')
58+
59+
if __name__ == "__main__":
60+
assert(test_singularity(singleton_simple.Singleton.getInstance))
61+
assert(test_singularity(singleton_new.Singleton))
62+
assert(test_singularity(singleton_new.SingletonN))
63+
assert(test_singularity(singleton_call.SingletonC))
64+
assert(test_singularity(singleton_deco.SingletonD))
65+
test_multiprocess_singleton(singleton_mp.SingletonS)
66+
67+

singletons/__init__.py

Whitespace-only changes.

singletons/singleton_call.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
""" Singleton by overriding the __call__ method on metaclass """
2+
3+
class MetaSingletonC(type):
4+
""" A type for Singleton classes (overrides __call__) """
5+
6+
def __init__(cls, *args):
7+
print(cls,"__init__ method called with args", args)
8+
type.__init__(cls, *args)
9+
cls.instance = None
10+
11+
def __call__(cls, *args, **kwargs):
12+
if not cls.instance:
13+
print(cls,"creating instance", args, kwargs)
14+
cls.instance = type.__call__(cls, *args, **kwargs)
15+
return cls.instance
16+
17+
class SingletonC(metaclass=MetaSingletonC):
18+
""" A singleton class using the MetaSingletonN class as metaclass """
19+
pass

singletons/singleton_deco.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
""" Singleton by using decorators """
2+
3+
def singleton(func):
4+
""" Singleton as a method decorator """
5+
6+
def wrapper(klass, *args):
7+
8+
if not hasattr(klass, 'instance') or klass.instance is None:
9+
klass.instance = object.__new__(klass)
10+
11+
return klass.instance
12+
13+
return wrapper
14+
15+
class SingletonD(object):
16+
17+
@singleton
18+
def __new__(cls):
19+
pass

singletons/singleton_mp.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Singleton on shared memory using multiprocessing module.
3+
Using this singleton, two processes can share the same object
4+
at the same memory location.
5+
6+
This is done by using the Manager object and its shared dictionary type.
7+
8+
"""
9+
10+
from multiprocessing import Manager
11+
12+
class SingletonS(object):
13+
""" A shared memory singleton class """
14+
15+
def __init__(self, x=100):
16+
mgr = Manager()
17+
self._shared = mgr.dict()
18+
self.x = x
19+
20+
def __setattr__(self, key, value):
21+
# For all lookups other than 'd'
22+
# set value in the shared dictionary
23+
if key == '_shared':
24+
self.__dict__[key] = value
25+
else:
26+
self._shared[key] = value
27+
28+
def __getattr__(self, key):
29+
if key in self.__dict__:
30+
return self.__dict__[key]
31+
32+
return self._shared[key]
33+

singletons/singleton_new.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
""" Singleton by overriding the __new__ method on Python classes
2+
on class as well as meta-class """
3+
4+
5+
# At meta-class level
6+
class MetaSingletonN(type):
7+
""" A type for Singleton classes by overriding __new__ """
8+
9+
def my_new(cls,name,bases=(),dct={}):
10+
if not cls.instance:
11+
cls.instance = object.__new__(cls)
12+
13+
return cls.instance
14+
15+
def __init__(cls, name, bases, dct):
16+
super(MetaSingletonN, cls).__init__(name, bases, dct)
17+
cls.instance = None
18+
cls.__new__ = cls.my_new
19+
20+
class Singleton(object):
21+
""" By overriding the __new__ method of the class and
22+
using dictionaries """
23+
24+
def __new__(cls):
25+
if '_instance' not in cls.__dict__:
26+
cls._instance = object.__new__(cls)
27+
return cls.__dict__['_instance']
28+
29+
class SingletonN(metaclass=MetaSingletonN):
30+
""" A singleton class using the MetaSingletonN class as metaclass """
31+
pass

singletons/singleton_simple.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
A basic implementation of Singleton -in a pattern similar to how its
3+
implemented in more traditional languages
4+
"""
5+
6+
class Singleton(object):
7+
""" A basic implementation of Singleton """
8+
9+
instance = None
10+
11+
@classmethod
12+
def getInstance(cls):
13+
14+
if cls.instance == None:
15+
cls.instance = cls()
16+
return cls.instance
17+

0 commit comments

Comments
 (0)