[Python自学] 设计模式之单例模式
一、几种方式实现单例模式
1.基本版的单例模式
class Foo(object): instance = None def __init__(self): pass @classmethod def get_instance(cls): if cls.instance: return cls.instance else: cls.instance = cls() return cls.instance def process(self): print("process") if __name__ == '__main__': obj1 = Foo.get_instance() obj2 = Foo.get_instance() print(id(obj1), id(obj2))
这个版本,我们要使用Foo.get_instance()来获得实例,对用户不友好。
2.基于__new__实现的单例模式
class Foo(object): instance = None def __init__(self): pass def __new__(cls, *args, **kwargs): if cls.instance: return cls.instance else: cls.instance = object.__new__(cls, *args, **kwargs) return cls.instance def process(self): print("process") if __name__ == '__main__': obj1 = Foo() obj2 = Foo() print(id(obj1), id(obj2))
这个版本,换成了用Foo()来获取实例,和非单例模式是一样的方式,屏蔽了底层细节,对用户友好。
3.基于模块导入机制实现单例模式
核心概念:模块只被导入一次
当我们在导入一个模块时,python会编译生成一个pyc文件,这个文件会在模块的第一次导入时生成。
当pyc存在的时候,例如第二次导入模块,则不会在运行模块中的代码,而是直接使用内存中的pyc。
所以,利用这一特性,我们可以构建单例模型:
# mysingleton.py class MySingleton(object): def foo(self): print("foo...") my_singleton = MySingleton()
在mysingleton模块中,定义了MySingleton类,并获取了一个实例对象。
在另一个模块中,对mysingleton模块进行导入:
# other.py # 第一次导入my_singleton from mysingleton import my_singleton # 第二次导入my_singleton from mysingleton import my_singleton as my_singleton_2 print(id(my_singleton)) print(id(my_singleton_2))
输出结果是两个对象的id相同,即两个对象实际上是同一个对象。
这是因为第一次导入的my_singleton处于内存中,当第二次导入同样的模块时,发现内存中已经存在,则直接引用了内存中的my_singleton对象。
这样就实现了基于模块的单例模型。
4.基于装饰器实现的单例模式
# 实现构建单例对象的装饰器 def singleton(cls, *args, **kwargs): # 一个用于存放单例对象的字典,以类为key,单例对象为value instances = {} def get_instance(*args, **kwargs): # 如果这个类没有创建过对象,则创建对象,作为value存放在字典中 if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class Test(object): def __init__(self, name): self.name = name def get_name(self): return self.name if __name__ == '__main__': t1 = Test("leo1") t2 = Test("leo2") print(t1.get_name()) # 打印leo1 print(t2.get_name()) # 也打印leo1 print(id(t1) == id(t2)) # 打印True,说明是同一个对象
对类使用装饰器,用装饰器来判断类是否已经产生了单例对象,如果没有则创建对象,如果已经存在,则直接返回已经存在的对象。
二、单例模式示例
在Django的admin.py中,用于注册model的 admin.site 就是一个单例对象:
首先,在admin的源码中,导入了site:
# ACTION_CHECKBOX_NAME is unused, but should stay since its import from here # has been referenced in documentation. from django.contrib.admin.decorators import register from django.contrib.admin.filters import ( AllValuesFieldListFilter, BooleanFieldListFilter, ChoicesFieldListFilter, DateFieldListFilter, FieldListFilter, ListFilter, RelatedFieldListFilter, RelatedOnlyFieldListFilter, SimpleListFilter, ) from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.admin.options import ( HORIZONTAL, VERTICAL, ModelAdmin, StackedInline, TabularInline, ) from django.contrib.admin.sites import AdminSite, site from django.utils.module_loading import autodiscover_modules __all__ = [ "register", "ACTION_CHECKBOX_NAME", "ModelAdmin", "HORIZONTAL", "VERTICAL", "StackedInline", "TabularInline", "AdminSite", "site", "ListFilter", "SimpleListFilter", "FieldListFilter", "BooleanFieldListFilter", "RelatedFieldListFilter", "ChoicesFieldListFilter", "DateFieldListFilter", "AllValuesFieldListFilter", "RelatedOnlyFieldListFilter", "autodiscover", ] def autodiscover(): autodiscover_modules('admin', register_to=site) default_app_config = 'django.contrib.admin.apps.AdminConfig'
然后,我们再看site的源码:(在sites.py中)
class DefaultAdminSite(LazyObject): def _setup(self): AdminSiteClass = import_string(apps.get_app_config('admin').default_site) self._wrapped = AdminSiteClass() # This global object represents the default admin site, for the common case. # You can provide your own AdminSite using the (Simple)AdminConfig.default_site # attribute. You can also instantiate AdminSite in your own code to create a # custom admin site. site = DefaultAdminSite()
可以看到,site是DefaultAdminSite类的一个对象。
当admin导入了site时,实际上就利用了第三种方式实现的单例模式(基于模块导入实现的单例模式)。
所以,我们在使用admin.site.register()来注册model的时候,使用的site是一个单例对象:
#app/admin.py中 admin.site.register(Host, HostConfig) admin.site.register(Business) admin.site.register(Application)
6
保持学习,否则迟早要被淘汰*(^ 。 ^ )***