1616from typing import Optional
1717
1818
19- class Singleton :
19+ class SingletonMeta ( type ) :
2020"""
21- EN: The Singleton class defines the `getInstance` method that lets clients
22- access the unique singleton instance.
21+ EN: This is a thread-safe implementation of Singleton.
2322
24- RU: Класс Одиночка предоставляет метод getInstance, который позволяет
25- клиентам получить доступ к уникальному экземпляру одиночки.
23+ RU: Это потокобезопасная реализация класса Singleton.
2624 """
2725
2826_instance :Optional [Singleton ]= None
2927
3028_lock :Lock = Lock ()
29+ """
30+ We now have a lock object that will be used to synchronize
31+ threads during first access to the Singleton.
32+
33+ RU: У нас теперь есть объект-блокировка для синхронизации потоков во
34+ время первого доступа к Одиночке.
35+ """
3136
32- value :str
33-
37+ def __call__ (cls ,* args ,** kwargs ):
38+ # EN: Now, imagine that the program has just been launched.
39+ # Since there's no Singleton instance yet, multiple threads can
40+ # simultaneously pass the previous conditional and reach this
41+ # point almost at the same time. The first of them will acquire
42+ # lock and will proceed further, while the rest will wait here.
43+ #
44+ # RU: Теперь представьте, что программа была только-только
45+ # запущена. Объекта-одиночки ещё никто не создавал, поэтому
46+ # несколько потоков вполне могли одновременно пройти через
47+ # предыдущее условие и достигнуть блокировки. Самый быстрый
48+ # поток поставит блокировку и двинется внутрь секции, пока
49+ # другие будут здесь его ожидать.
50+ with cls ._lock :
51+ # EN: The first thread to acquire the lock, reaches this
52+ # conditional, goes inside and creates the Singleton
53+ # instance. Once it leaves the lock block, a thread that
54+ # might have been waiting for the lock release may then
55+ # enter this section. But since the Singleton field is
56+ # already initialized, the thread won't create a new
57+ # object.
58+ #
59+ # RU: Первый поток достигает этого условия и проходит внутрь,
60+ # создавая объект-одиночку. Как только этот поток покинет
61+ # секцию и освободит блокировку, следующий поток может
62+ # снова установить блокировку и зайти внутрь. Однако теперь
63+ # экземпляр одиночки уже будет создан и поток не сможет
64+ # пройти через это условие, а значит новый объект не будет
65+ # создан.
66+ if not cls ._instance :
67+ cls ._instance = super ().__call__ (* args ,** kwargs )
68+ return cls ._instance
69+
70+
71+ class Singleton (metaclass = SingletonMeta ):
72+ value :str = None
73+ """
74+ EN: We'll use this property to prove that our Singleton really works.
75+
76+ RU: Мы используем это поле, чтобы доказать, что наш Одиночка
77+ действительно работает.
78+ """
79+
3480def __init__ (self ,value :str )-> None :
3581self .value = value
3682
37- @staticmethod
38- def get_instance (value :str )-> Singleton :
39- """
40- EN: The static method that controls the access to the singleton
41- instance.
42-
43- This implementation let you subclass the Singleton class while keeping
44- just one instance of each subclass around.
45-
46- RU: Статический метод, управляющий доступом к экземпляру одиночки.
47-
48- Эта реализация позволяет вам расширять класс Одиночки, сохраняя повсюду
49- только один экземпляр каждого подкласса.
50- """
51-
52- if not Singleton ._instance :
53- with Singleton ._lock :
54- if not Singleton ._instance :
55- Singleton ._instance = Singleton (value )
56- return Singleton ._instance
57-
5883def some_business_logic (self ):
5984"""
6085 EN: Finally, any singleton should define some business logic, which can
@@ -64,11 +89,9 @@ def some_business_logic(self):
6489 которая может быть выполнена на его экземпляре.
6590 """
6691
67- # ...
68-
6992
7093def test_singleton (value :str )-> None :
71- singleton = Singleton . get_instance (value )
94+ singleton = Singleton (value )
7295print (singleton .value )
7396
7497