Python 使用装饰器的单例
Python Singleton using decorator
下面的装饰器如何使 Database
成为单例?
我理解的方式是 @singleton
装饰器使 Database
class 成为 returns 一个 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?
输出
{} 4011784
Loading database
{<class '__main__.Database'>: <__main__.Database object at 0x0577C148>} 4011784
有几个概念共同使这成为可能。让我们看看您当前的代码:
@singleton
class Database:
def __init__(self):
print('Loading database')
上面的代码可以翻译成下面的代码。 @
语法是以下形式的缩写:
class Database:
def __init__(self):
print('Loading database')
print(Database)
Database = singleton(Database) # manually decorated
print(Database)
class 声明本身存储到名为 Database
的变量中。之后,变量被替换为 singleton
函数调用(即 get_instance
函数)的结果。这意味着,每当您创建 Database
的新实例时,实际上现在会调用 get_instance
。
<class '__main__.Database'>
<function singleton.<locals>.get_instance at 0x7f8e82738310>
现在让我们来看看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
singleton
函数声明了一个属于singleton
函数作用域的局部instances
变量。每次调用 singleton
都会创建一个新的独立 get_instance
函数(所谓的闭包)。根据LEGB rule,封闭范围的变量是“inherit”(这在技术上是不正确的,但简化了解释)到闭包。这意味着,instances
将在每次调用特定 get_instance
函数时保持相同的对象。
下面我们来总结一下。由于每次调用 Database()
实际上 get_instance
被调用,并且由于 instances
每次调用这个特定的 get_instance
函数都保持不变,我们可以达到这样的效果,即class 只会创建一次,并且每次后续调用都会返回相同的对象。
下面的装饰器如何使 Database
成为单例?
我理解的方式是 @singleton
装饰器使 Database
class 成为 returns 一个 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?
输出
{} 4011784
Loading database
{<class '__main__.Database'>: <__main__.Database object at 0x0577C148>} 4011784
有几个概念共同使这成为可能。让我们看看您当前的代码:
@singleton
class Database:
def __init__(self):
print('Loading database')
上面的代码可以翻译成下面的代码。 @
语法是以下形式的缩写:
class Database:
def __init__(self):
print('Loading database')
print(Database)
Database = singleton(Database) # manually decorated
print(Database)
class 声明本身存储到名为 Database
的变量中。之后,变量被替换为 singleton
函数调用(即 get_instance
函数)的结果。这意味着,每当您创建 Database
的新实例时,实际上现在会调用 get_instance
。
<class '__main__.Database'>
<function singleton.<locals>.get_instance at 0x7f8e82738310>
现在让我们来看看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
singleton
函数声明了一个属于singleton
函数作用域的局部instances
变量。每次调用 singleton
都会创建一个新的独立 get_instance
函数(所谓的闭包)。根据LEGB rule,封闭范围的变量是“inherit”(这在技术上是不正确的,但简化了解释)到闭包。这意味着,instances
将在每次调用特定 get_instance
函数时保持相同的对象。
下面我们来总结一下。由于每次调用 Database()
实际上 get_instance
被调用,并且由于 instances
每次调用这个特定的 get_instance
函数都保持不变,我们可以达到这样的效果,即class 只会创建一次,并且每次后续调用都会返回相同的对象。