Introduction
Of all of the design patterns, the Singleton sample holds a novel place. It is easy, but is commonly misunderstood. On this Byte, we’ll attempt to clarify the Singleton sample, perceive its core rules, and discover ways to implement it in Python. We’ll additionally discover tips on how to create a Singleton utilizing a decorator.
The Singleton Sample
The Singleton sample is a design sample that restricts the instantiation of a category to a single occasion. That is helpful when precisely one object is required to coordinate actions throughout the system. The idea is typically generalized to programs that function extra effectively when just one object exists, or that prohibit the instantiation to a sure variety of objects.
The Singleton sample is part of the Gang of 4 design patterns and falls below the class of creational patterns. Creational patterns take care of object creation mechanisms, attempting to create objects in a way appropriate to the state of affairs.
Notice: The Singleton sample is taken into account an anti-pattern by some attributable to its potential for misuse. It is necessary to make use of it judiciously and solely when obligatory.
Making a Singleton in Python
Python would not natively help the Singleton sample, however there are a number of methods to create one. This is a easy instance:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = tremendous(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
Within the above code, we override the __new__
technique. This technique is known as earlier than __init__
when an object is created. If the Singleton class’s _instance
attribute is None
, we create a brand new Singleton object and assign it to _instance
. If _instance
is already set, we return that as an alternative.
Utilizing this method successfully solely permits the Singleton
class to be instantiated as soon as. You possibly can then add any properties or strategies to this class that you just want.
Utilizing a Decorator
One other strategy to create a Singleton in Python is through the use of a decorator. Decorators enable us to wrap one other perform with the intention to lengthen the habits of the wrapped perform, with out completely modifying it.
This is how we are able to create a Singleton utilizing a decorator:
def singleton(cls):
situations = {}
def wrapper(*args, **kwargs):
if cls not in situations:
situations[cls] = cls(*args, **kwargs)
return situations[cls]
return wrapper
@singleton
class Singleton:
cross
Within the above code, the @singleton
decorator checks if an occasion of the category it is adorning exists within the situations
dictionary. If it would not, it creates one and provides it to the dictionary. If it does exist, it merely returns the present occasion.
Utilizing a Base Class
Making a singleton utilizing a base class is an easy strategy. Right here, we outline a base class that maintains a dictionary of occasion references. At any time when an occasion is requested, we first examine if the occasion already exists within the dictionary. If it does, we return the present occasion, in any other case, we create a brand new occasion and retailer its reference within the dictionary.
This is how one can implement a singleton utilizing a base class in Python:
class SingletonBase:
_instances = {}
def __new__(cls, *args, **kwargs):
if cls not in cls._instances:
occasion = tremendous().__new__(cls)
cls._instances[cls] = occasion
return cls._instances[cls]
class Singleton(SingletonBase):
cross
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)
Within the above code, SingletonBase
is the bottom class that implements the singleton sample. Singleton
is the category that we wish to make a singleton.
Utilizing a Metaclass
A metaclass in Python is a category of a category, that means a category is an occasion of its metaclass. We are able to use a metaclass to create a singleton by overriding its __call__
technique to regulate the creation of situations.
This is how one can implement a singleton utilizing a metaclass in Python:
class SingletonMeta(sort):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
occasion = tremendous().__call__(*args, **kwargs)
cls._instances[cls] = occasion
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
cross
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)
Within the above code, SingletonMeta
is the metaclass that implements the singleton sample. Singleton
is the category that we wish to make a singleton.
Use Circumstances
Singletons are helpful when it’s worthwhile to management entry to a useful resource or when it’s worthwhile to restrict the instantiation of a category to a single object. That is usually helpful in situations reminiscent of logging, driver objects, caching, thread swimming pools, and database connections.
Singleton sample is taken into account an anti-pattern by some attributable to its international nature and the potential for unintended uncomfortable side effects. Remember to use it solely when obligatory!
Singletons and Multithreading
When coping with multithreading, singletons might be tough. If two threads attempt to create an occasion on the similar time, they could find yourself creating two totally different situations. To forestall this, we have to synchronize the occasion creation course of.
This is how one can deal with singleton creation in a multithreaded setting:
Try our hands-on, sensible information to studying Git, with best-practices, industry-accepted requirements, and included cheat sheet. Cease Googling Git instructions and really be taught it!
import threading
class SingletonMeta(sort):
_instances = {}
_lock: threading.Lock = threading.Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if cls not in cls._instances:
occasion = tremendous().__call__(*args, **kwargs)
cls._instances[cls] = occasion
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
cross
def test_singleton():
s1 = Singleton()
print(s1)
threads = [threading.Thread(target=test_singleton) for _ in range(10)]
for thread in threads:
thread.begin()
for thread in threads:
thread.be a part of()
Within the above code, we use a lock to make sure that just one thread can create an occasion at a time. This prevents the creation of a number of singleton situations in a multithreaded setting.
Widespread Pitfalls
Whereas singletons could be a highly effective software in your Python programming toolkit, they don’t seem to be with out their pitfalls. Listed here are a couple of frequent ones to remember:
-
International Variables: Singleton can generally be misused as a worldwide variable. This could result in issues because the state of the singleton might be modified by any a part of the code, resulting in unpredictable habits.
-
Testability: Singletons could make unit testing troublesome. Since they preserve state between calls, a check might doubtlessly modify that state and have an effect on the end result of different exams. That is why it is necessary to make sure that the state is reset earlier than every check.
-
Concurrency Points: In a multithreaded setting, care should be taken to make sure that the singleton occasion is simply created as soon as. If not correctly dealt with, a number of threads might doubtlessly create a number of situations.
This is an instance of how a singleton could cause testing points:
class Singleton(object):
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = tremendous(Singleton, cls).__new__(cls)
return cls._instance
s1 = Singleton()
s2 = Singleton()
s1.x = 5
print(s2.x)
On this case, should you had been to check the habits of Singleton
and modify x
, that change would persist throughout all situations and will doubtlessly have an effect on different exams.
Conclusion
Singletons are a design sample that restricts a category to a single occasion. They are often helpful in situations the place a single shared useful resource, reminiscent of a database connection or configuration file, is required. In Python, you possibly can create a singleton utilizing varied strategies reminiscent of decorators, base lessons, and metaclasses.
Nevertheless, singletons include their very own set of pitfalls, together with misuse as international variables, difficulties in testing, and concurrency points in multithreaded environments. It is necessary to pay attention to these points and use singletons judiciously.