[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'
View Code

然后,我们再看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

posted @ 2020-01-21 17:15  风间悠香  阅读(261)  评论(0编辑  收藏  举报