Skip to content

Commit 308cf47

Browse files
committed
fix Singleton inheritance
- Fix the previous behavior of Singleton, when the same instance is returned for each possible subclass. Now one instance for each subclass. - Add an explanation about possible changes to the value of the `__init__` argument. - Remove `from __future__ import annotations` because of Python3.7+ requirements declared in `README`. - Fix according to PEP-8 (80 symbols).
1 parent 14eacc5 commit 308cf47

File tree

2 files changed

+33
-21
lines changed

2 files changed

+33
-21
lines changed

src/Singleton/Conceptual/NonThreadSafe/main.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,16 @@
22
EN: Singleton Design Pattern
33
44
Intent: Lets you ensure that a class has only one instance, while providing a
5-
global access point to this instance.
5+
global access point to this instance. One instance per each subclass (if any).
66
77
RU: Паттерн Одиночка
88
99
Назначение: Гарантирует, что у класса есть только один экземпляр, и
10-
предоставляет к нему глобальную точку доступа.
10+
предоставляет к нему глобальную точку доступа. У каждого наследника класса тоже
11+
будет по одному экземпляру.
1112
"""
1213

1314

14-
from __future__ import annotations
15-
from typing import Optional
16-
17-
1815
class SingletonMeta(type):
1916
"""
2017
EN: The Singleton class can be implemented in different ways in Python. Some
@@ -26,12 +23,20 @@ class SingletonMeta(type):
2623
метаклассом, поскольку он лучше всего подходит для этой цели.
2724
"""
2825

29-
_instance: Optional[Singleton] = None
26+
_instances = {}
3027

31-
def __call__(self) -> Singleton:
32-
if self._instance is None:
33-
self._instance = super().__call__()
34-
return self._instance
28+
def __call__(cls, *args, **kwargs):
29+
"""
30+
EN: Possible changes to the value of the `__init__` argument do not
31+
affect the returned instance.
32+
33+
RU: Данная реализация не учитывает возможное изменение передаваемых
34+
аргументов в `__init__`.
35+
"""
36+
if cls not in cls._instances:
37+
instance = super().__call__(*args, **kwargs)
38+
cls._instances[cls] = instance
39+
return cls._instances[cls]
3540

3641

3742
class Singleton(metaclass=SingletonMeta):

src/Singleton/Conceptual/ThreadSafe/main.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22
EN: Singleton Design Pattern
33
44
Intent: Lets you ensure that a class has only one instance, while providing a
5-
global access point to this instance.
5+
global access point to this instance. One instance per each subclass (if any).
66
77
RU: Паттерн Одиночка
88
99
Назначение: Гарантирует, что у класса есть только один экземпляр, и
10-
предоставляет к нему глобальную точку доступа.
10+
предоставляет к нему глобальную точку доступа. У каждого наследника класса тоже
11+
будет по одному экземпляру.
1112
"""
1213

13-
14-
from __future__ import annotations
1514
from threading import Lock, Thread
16-
from typing import Optional
1715

1816

1917
class SingletonMeta(type):
@@ -23,7 +21,7 @@ class SingletonMeta(type):
2321
RU: Это потокобезопасная реализация класса Singleton.
2422
"""
2523

26-
_instance: Optional[Singleton] = None
24+
_instances = {}
2725

2826
_lock: Lock = Lock()
2927
"""
@@ -35,6 +33,13 @@ class SingletonMeta(type):
3533
"""
3634

3735
def __call__(cls, *args, **kwargs):
36+
"""
37+
EN: Possible changes to the value of the `__init__` argument do not
38+
affect the returned instance.
39+
40+
RU: Данная реализация не учитывает возможное изменение передаваемых
41+
аргументов в `__init__`.
42+
"""
3843
# EN: Now, imagine that the program has just been launched.
3944
# Since there's no Singleton instance yet, multiple threads can
4045
# simultaneously pass the previous conditional and reach this
@@ -63,9 +68,10 @@ def __call__(cls, *args, **kwargs):
6368
# экземпляр одиночки уже будет создан и поток не сможет
6469
# пройти через это условие, а значит новый объект не будет
6570
# создан.
66-
if not cls._instance:
67-
cls._instance = super().__call__(*args, **kwargs)
68-
return cls._instance
71+
if cls not in cls._instances:
72+
instance = super().__call__(*args, **kwargs)
73+
cls._instances[cls] = instance
74+
return cls._instances[cls]
6975

7076

7177
class Singleton(metaclass=SingletonMeta):
@@ -101,7 +107,8 @@ def test_singleton(value: str) -> None:
101107
# RU: Клиентский код.
102108

103109
print("If you see the same value, then singleton was reused (yay!)\n"
104-
"If you see different values, then 2 singletons were created (booo!!)\n\n"
110+
"If you see different values, "
111+
"then 2 singletons were created (booo!!)\n\n"
105112
"RESULT:\n")
106113

107114
process1 = Thread(target=test_singleton, args=("FOO",))

0 commit comments

Comments
 (0)