第6章 使用一等函数实现设计模式

[toc]

设计模式
每个设计模式都是围绕如下三个问题:

1.为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的问题?
2.是什么?通过Python语言来去实现这个设计模式,用于解决为什么中提到的问题。
3.怎么用?理解了为什么我们也就基本了解了什么情况下使用这个模式,不过在这里还是会细化使用场景,阐述模式的局限和优缺点

单例模式

这一篇我们先来看看单例模式。单例模式是设计模式中逻辑最简单,最容易理解的一个模式,简单到只需要一句话就可以理解,即保证只有一个对象实例的模式

问题的关键在于实现起来并没有想象的那么简单。

不过我们还是先来讨论下为什么需要这个模式吧。

为什么

我们首先来看看单例模式的使用场景,然后再来分析为什么需要单例模式。

  • Python的logger就是一个单例模式,用以日志记录
  • Windows的资源管理器是一个单例模式
  • 线程池,数据库连接池等资源池一般也用单例模式
  • 网站计数器


从这些使用场景我们可以总结下什么情况下需要单例模式:
1.当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;

2.当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。



当然所有使用单例模式的前提是我们的确用一个实例就可以搞定要解决的问题,而不需要多个实例,如果每个实例都需要维护自己的状态,这种情况下单例模式肯定是不适用的。

接下来看看如何使用Python来实现一个单例模式。

是什么

最开始的想法很简单,实现如下:

class Singleton(object):
    __instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwd):
        if Singleton.__instance is None:
            Singleton.__instance = object.__new__(cls, *args, **kwd)
        return Singleton.__instance


s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2)

if s1 == s2:
    print("True")
else:
    print("False")

运行结果是:

<__main__.Singleton object at 0x000002176FE884A8>
<__main__.Singleton object at 0x000002176FE884A8>
True

怎么用

在 Python 中,我们可以用多种方法来实现单例模式:

使用模块
使用 __new__
使用装饰器(decorator)
使用元类(metaclass)

1.使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。

因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
如果我们真的想要一个单例类,可以考虑这样做:

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass


my_singleton = My_Singleton()

# 将上面的代码保存在文件 mysingleton.py 中,然后这样使用:

from mysingleton import my_singleton
my_singleton.foo()


2.使用__new__方法
为了使类只能出现一个实例,我们可以使用 new 来控制实例的创建过程,代码如下:

class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kw):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
        return cls._instance


class MyClass(Singleton):
    a = 1


one = MyClass()
two = MyClass()

print(one == two)
print(one is two)
print(id(one), id(two))

运行结果:

True
True
2483856443152 2483856443152



3.使用装饰器
我们知道,装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下:

from functools import wraps


def singleton(cls):
    instances = {}

    @wraps(cls)
    def getinstance(*args, **kw):
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]

    return getinstance


@singleton
class MyClass(object):
    a = 1


one = MyClass()
two = MyClass()

print(one == two)

在上面,我们定义了一个装饰器 singleton,它返回了一个内部函数 getinstance,该函数会判断某个类是否在字典 instances 中,

如果不存在,则会将 cls 作为 key,cls(args, *kw) 作为 value 存到 instances 中,否则,直接返回 instances[cls]。



4.使用元类

元类(metaclass)可以控制类的创建过程,它主要做三件事:

拦截类的创建
修改类的定义
返回修改后的类
使用元类实现单例模式的代码如下:

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton,
                                        cls).__call__(*args, **kwargs)
        return cls._instances[cls]


# # Python2
# class MyClass(object):
#     __metaclass__ = Singleton


#Python3
class MyClass(metaclass=Singleton):
    pass


one = MyClass()
two = MyClass()

print(one == two)

王知晓
37 声望19 粉丝

知晓知晓,上知天文下晓地理