Python Singleton using decorator
How is the below decorator make Database
a singleton?
The way I am understanding it is that the @singleton
decorator makes the Database
class to a function that returns a Database
object. However, since it's now acting like a function, when the function call ends, function variables should be dead (e.g. instances
variable).
The weird part is that when I call this function the second time, it seems like the instance
variable is still alive and store the previous information, even id
is the same. How is this happening? Can someone please help me understand this?
Thank you in advance
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
nonlocal instances
print(instances, id(instances))
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print('Loading database')
if __name__ == '__main__':
db1 = Database() # first call.
db2 = Database() # second call. why is this returning the same id?
Output
{} 4011784
Loading database
{<class '__main__.Database'>: <__main__.Database object at 0x0577C148>} 4011784
Solution 1:
There are several concepts which together make this possible. Let's take a look at your current code:
@singleton
class Database:
def __init__(self):
print('Loading database')
The above code can be translated to the following code. The @
syntax is the short form of:
class Database:
def __init__(self):
print('Loading database')
print(Database)
Database = singleton(Database) # manually decorated
print(Database)
The class declaration itself is stored to the variable named Database
. Afterwards, the variable is replaced with the result of the singleton
function call (which is the get_instance
function). This means, whenever you create a new instance of Database
actually get_instance
is now called.
<class '__main__.Database'>
<function singleton.<locals>.get_instance at 0x7f8e82738310>
Now let's take a look at the singleton
function:
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
nonlocal instances # nonlocal is not required here!
print(instances, id(instances))
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
The singleton
function declares a local instances
variable which belongs to the scope of the singleton
function. On each call to singleton
a newly independent get_instance
function (a so-called closure) is created. According to the LEGB rule, the variables of the enclosing scope are "inherit" (this is technically not correct, but simplifies the explanation) to the closure. This means, that instances
will stay the same object for every call to a specific get_instance
function.
Now let's summarize. As on each call of Database()
actually get_instance
is called, and as instances
stays the same for each call to this specific get_instance
function, we can achieve the effect, that the class will be created only once and that the same object will be returned for every succeeding call.