-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsingleton.py
More file actions
121 lines (99 loc) · 4 KB
/
singleton.py
File metadata and controls
121 lines (99 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
'''
This script shows an easy way to implement singleton pattern using decorators python feature.
Extracted from: https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html
Original idea by Chih-Chung Chang modified by Vykstorm
'''
from inspect import isclass
class _Singleton:
'''
This is a class decorator (to decorate classes also) to implement the singleton pattern.
Dont use this decorator directly, instead you can use the singleton method as decorator, see below.
'''
def __init__(self, cls, *args, **kwargs):
'''
Initializes this instance.
:param cls: It must be the class to be decorated
:param args: Additional position arguments to be included at singleton initialization
:param kwargs: Additional keyword arguments to be included at singleton initialization
'''
if not isclass(cls):
raise TypeError('Expected class at argument 1, got {}'.format(type(cls).__name__))
self.cls = cls
self.instance = None
self.args = args
self.kwargs = kwargs
def __call__(self):
'''
This is called each time a new object of the decorated class need to be instantiated.
It creates the singleton object if it doesnt exist yet and return it passing the args and kwargs indicated at
this instance initialization as arguments to the singleton constructor.
'''
if self.instance is None:
self.instance = self.cls(*self.args, **self.kwargs)
return self.instance
def singleton(*args, **kwargs):
'''
This is a method that decorates class objects to implement singleton pattern.
The next syntaxes can be used to mark a class as a singleton using this decorator:
@singleton
class Foo:
...
@singleton(args = [...], kwargs = {...})
class Foo:
...
In the first example, singleton object constructor will not take any arguments.
On the other, args (which must be an iterable object) places positional arguments in the singleton object
constructor.
Also kwargs (dictionary) entries will be sent as keyword arguments on the constructor.
e.g:
@singleton(args = (1,2,3), kwargs = {'x':4,'y':5})
class Foo:
def __init__(a,b,c, x, y):
print(a+b+c, x+y)
Foo() will print "6, 9"
:param args:
:param kwargs:
:return:
'''
if len(args) == 1 and len(kwargs) == 0:
cls = args[0]
return _Singleton(cls)
if len(args) > 0:
raise ValueError('Invalid decorator syntax. It must be: @singleton, @singleton() or @singleton([args = (...)], [kwargs = {...}])')
if len(kwargs) == 0 or any([kwarg not in ('args', 'kwargs') for kwarg in kwargs]):
invalid_args = [kwarg for kwarg in kwargs if kwarg not in ('args', 'kwargs')]
raise TypeError('Unexpected decorator argument{}: "{}"'.format(
's' if len(invalid_args) > 1 else '', ', '.join(invalid_args)))
_args = tuple(kwargs['args']) if 'args' in kwargs else ()
_kwargs = dict(kwargs['kwargs']) if 'kwargs' in kwargs else {}
def _singleton(cls):
return _Singleton(cls, *_args, **_kwargs)
return _singleton
if __name__ == '__main__':
'''
This example illustrates the usage of singleton decorator
'''
@singleton(kwargs = {'limit': 900})
class PerfectSquares:
def __init__(self, limit=577):
self.index = 1
self.current = 1
self.limit = limit
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
val = self.current
self.index += 1
self.current += (self.index << 1) - 1
return val
else:
raise StopIteration()
A = iter(PerfectSquares())
B = iter(PerfectSquares())
print('Perfect square numbers: ')
try:
while True:
print('{:4d} {:4d} '.format(next(A), next(B)), end='')
except StopIteration:
pass