Python设计模式之单例模式

目录

 

单例模式定义:

import 模块:

logging模块

实现单例模式的几种方式:

1.使用模块方法

2.使用装饰器

3.使用类

4.基于__new__方法实现(推荐使用,方便)

5.基于metaclass方式实现


单例模式定义:

维基百科对单例模式定义如下:

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

import 模块:

其实python的import module就是一种典型的单例模式。请看下面这个例子:

# global_var.py
COUNT = 0

我们写一个多线程的测试程序,每个线程都把COUNT加一,并且打印出来

In [1]: from multiprocessing.dummy import Pool

In [2]: p = Pool(10)

In [3]: def count(x):
   ...:     import global_var
   ...:     myglobal.COUNT += 1
   ...:     print global_var.COUNT

In [4]: p.map(count, range(10))
1
 3
 5
2
6
7
 9
8
10
4
Out[4]: [None, None, None, None, None, None, None, None, None, None]

In [5]: import global_var

In [6]: global_var.COUNT
Out[6]: 10

可以看到,在每个线程中都把COUNT加了一,得到不同的值。在最后也按照预期得到了COUNT值为10。

我们再看一下每个线程里面global_var的内存地址,就明白为什么说它是单例模式了。

In [1]: from multiprocessing.dummy import Pool

In [2]: p = Pool(10)

In [3]: def imp(x):
    import global_var
    print id(global_var)
   ...:

In [4]: p.map(imp, range(10))
140519408948848
140519408948848
 140519408948848
140519408948848
 140519408948848
 140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
Out[4]: [None, None, None, None, None, None, None, None, None, None]

可以看到,print出来地址值是相同的。

现在让我们想想import的用法,就可以明白为什么它要设计成这样了。

我们都知道,import一个模块之后,会在程序的不同地方调用这个模块,可能在不同的函数,也可能在不同的线程中。我们希望无论在什么地方,这个模块的行为是一致的,也就是说,我们希望它是唯一的,而不是一个独立的个体。

logging模块

logging是单例模式的一个非常典型的应用,因为我们的应用内部一般不去关心logging的细节,比如写到数据库还是文件,时间戳的格式等等。在应用内部,我们只关心logging的分类,内容和级别,是app的还是db的log,是warning,error还是debug。

而logging的配置,则是全局的,一个进程中任何地方对logging配置的修改(如增加输出到文件),都会影响到所有使用logging的地方。那么它是怎么做到的呢?

例子:

import logging
# 全局的配置
logging.basicConfig(format="%(asctime)s %(levelname)s %(name)s %(message)s", level='DEBUG')
# 在函数中如下使用
logging.debug('hello world')

# 在其他module中,sub.py
import logging
logging.info('this is the test in module!')

打印结果为:

2018-10-17 10:26:33,100 DEBUG root hello world
2018-10-17 10:26:34,274 INFO root this is test in module!

上面两次log的输出格式一致, 让我们来看看logging的源码

root = RootLogger(WARNING)

def info(msg, *args, **kwargs):
    """
    Log a message with severity 'INFO' on the root logger.
    """
    if len(root.handlers) == 0:
        basicConfig()
    root.info(msg, *args, **kwargs)

可以看到具体的执行都是由root这个实例来进行的,由于root是一个module层面的实例,并不在info函数内部实例化,所以所有的logging.info都是同一个实例来进行的。

看到单例模式的使用场景, 那么我们如何自己实现单例模式呢?

实现单例模式的几种方式:

1.使用模块方法

既然Python 的模块就是天然的单例模式, 那我们可以利用模块这一特性轻松实现单例模式, 如下:

mysingleton.py

class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()
from mysingleton import singleton

2.使用装饰器

def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x


a1 = A(2)
a2 = A(3)

3.使用类

class Singleton(object):

    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

4.基于__new__方法实现(推荐使用,方便)

我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式


class Singleton(object):

    def __init__(self):
        pass


    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = object.__new__(cls)  
        return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton() 

5.基于metaclass方式实现

class SingletonType(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1, obj2)

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值