ido*_*ooo 2 python singleton decorator python-decorators
下面的装饰器是如何创建Database单例的?
我的理解是,@singleton装饰器将Database类创建为返回对象的函数Database。然而,由于它现在的行为就像一个函数,当函数调用结束时,函数变量应该是死的(例如instances变量)。
奇怪的是,当我第二次调用这个函数时,变量似乎instance仍然存在并存储以前的信息,甚至id是相同的。这是怎么发生的?有人可以帮我理解这一点吗?
先感谢您
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?
Run Code Online (Sandbox Code Playgroud)
输出
{} 4011784
Loading database
{<class '__main__.Database'>: <__main__.Database object at 0x0577C148>} 4011784
Run Code Online (Sandbox Code Playgroud)
有几个概念共同使这成为可能。让我们看一下您当前的代码:
@singleton
class Database:
def __init__(self):
print('Loading database')
Run Code Online (Sandbox Code Playgroud)
上面的代码可以翻译成下面的代码。语法@是以下缩写形式:
class Database:
def __init__(self):
print('Loading database')
print(Database)
Database = singleton(Database) # manually decorated
print(Database)
Run Code Online (Sandbox Code Playgroud)
类声明本身存储到名为 的变量中Database。然后,变量被函数调用的结果singleton(即函数get_instance)替换。这意味着,每当您创建一个新实例时,Database实际上get_instance都会被调用。
<class '__main__.Database'>
<function singleton.<locals>.get_instance at 0x7f8e82738310>
Run Code Online (Sandbox Code Playgroud)
现在我们来看看这个singleton函数:
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
Run Code Online (Sandbox Code Playgroud)
该函数声明了一个属于该函数作用域的singleton局部变量。每次调用时都会创建一个新的独立函数(所谓的闭包)。根据LEGB规则,封闭作用域的变量是“继承”(这在技术上是不正确的,但简化了解释)到闭包。这意味着,每次调用特定函数时都将保持相同的对象。instancessingletonsingletonget_instanceinstancesget_instance
现在我们来总结一下。由于每次调用Database()实际上get_instance都会被调用,并且instances每次调用此特定函数时都保持不变get_instance,因此我们可以实现这样的效果:该类将仅创建一次,并且每次后续调用都将返回相同的对象。