|
| 1 | +# 单例模式 |
| 2 | + |
| 3 | +**单例模式(Singleton Pattern)**是一种常用的软件设计模式,该模式的主要目的是确保**某一个类只有一个实例存在**。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 |
| 4 | + |
| 5 | +比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。 |
| 6 | + |
| 7 | +在 Python 中,我们可以用多种方法来实现单例模式: |
| 8 | + |
| 9 | +- 使用模块 |
| 10 | +- 使用 `__new__` |
| 11 | +- 使用装饰器(decorator) |
| 12 | +- 使用元类(metaclass) |
| 13 | + |
| 14 | +# 使用模块 |
| 15 | + |
| 16 | +其实,**Python 的模块就是天然的单例模式**,因为模块在第一次导入时,会生成 `.pyc` 文件,当第二次导入时,就会直接加载 `.pyc` 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做: |
| 17 | + |
| 18 | +```python |
| 19 | +# mysingleton.py |
| 20 | +class My_Singleton(object): |
| 21 | + def foo(self): |
| 22 | + pass |
| 23 | + |
| 24 | +my_singleton = My_Singleton() |
| 25 | +``` |
| 26 | + |
| 27 | +将上面的代码保存在文件 `mysingleton.py` 中,然后这样使用: |
| 28 | + |
| 29 | +``` |
| 30 | +from mysingleton import my_singleton |
| 31 | +
|
| 32 | +my_singleton.foo() |
| 33 | +``` |
| 34 | + |
| 35 | +# 使用 `__new__` |
| 36 | + |
| 37 | +为了使类只能出现一个实例,我们可以使用 `__new__` 来控制实例的创建过程,代码如下: |
| 38 | + |
| 39 | +```python |
| 40 | +class Singleton(object): |
| 41 | + _instance = None |
| 42 | + def __new__(cls, *args, **kw): |
| 43 | + if not cls._instance: |
| 44 | + cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) |
| 45 | + return cls._instance |
| 46 | + |
| 47 | +class MyClass(Singleton): |
| 48 | + a = 1 |
| 49 | +``` |
| 50 | + |
| 51 | +在上面的代码中,我们将类的实例和一个类变量 `_instance` 关联起来,如果 `cls._instance` 为 None 则创建实例,否则直接返回 `cls._instance`。 |
| 52 | + |
| 53 | +执行情况如下: |
| 54 | + |
| 55 | +``` |
| 56 | +>>> one = MyClass() |
| 57 | +>>> two = MyClass() |
| 58 | +>>> one == two |
| 59 | +True |
| 60 | +>>> one is two |
| 61 | +True |
| 62 | +>>> id(one), id(two) |
| 63 | +(4303862608, 4303862608) |
| 64 | +``` |
| 65 | + |
| 66 | +# 使用装饰器 |
| 67 | + |
| 68 | +我们知道,装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下: |
| 69 | + |
| 70 | +``` |
| 71 | +from functools import wraps |
| 72 | +
|
| 73 | +def singleton(cls): |
| 74 | + instances = {} |
| 75 | + @wraps(cls) |
| 76 | + def getinstance(*args, **kw): |
| 77 | + if cls not in instances: |
| 78 | + instances[cls] = cls(*args, **kw) |
| 79 | + return instances[cls] |
| 80 | + return getinstance |
| 81 | +
|
| 82 | +@singleton |
| 83 | +class MyClass(object): |
| 84 | + a = 1 |
| 85 | +``` |
| 86 | + |
| 87 | +在上面,我们定义了一个装饰器 `singleton`,它返回了一个内部函数 `getinstance`,该函数会判断某个类是否在字典 `instances` 中,如果不存在,则会将 `cls` 作为 key,`cls(*args, **kw)` 作为 value 存到 `instances` 中,否则,直接返回 `instances[cls]`。 |
| 88 | + |
| 89 | +# 使用 metaclass |
| 90 | + |
| 91 | +元类(metaclass)可以控制类的创建过程,它主要做三件事: |
| 92 | + |
| 93 | +- 拦截类的创建 |
| 94 | +- 修改类的定义 |
| 95 | +- 返回修改后的类 |
| 96 | + |
| 97 | +使用元类实现单例模式的代码如下: |
| 98 | + |
| 99 | +```python |
| 100 | +class Singleton(type): |
| 101 | + _instances = {} |
| 102 | + def __call__(cls, *args, **kwargs): |
| 103 | + if cls not in cls._instances: |
| 104 | + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) |
| 105 | + return cls._instances[cls] |
| 106 | + |
| 107 | +# Python2 |
| 108 | +class MyClass(object): |
| 109 | + __metaclass__ = Singleton |
| 110 | + |
| 111 | +# Python3 |
| 112 | +# class MyClass(metaclass=Singleton): |
| 113 | +# pass |
| 114 | +``` |
| 115 | + |
| 116 | +# 小结 |
| 117 | + |
| 118 | +- Python 的模块是天然的单例模式,这在大部分情况下应该是够用的,当然,我们也可以使用装饰器、元类等方法 |
| 119 | + |
| 120 | +# 参考资料 |
| 121 | + |
| 122 | +- [Creating a singleton in Python - Stack Overflow](http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python) |
| 123 | +- [深入浅出单实例Singleton设计模式 | 酷 壳](http://coolshell.cn/articles/265.html) |
| 124 | +- [design patterns - Python's use of __new__ and __init__? - Stack Overflow](http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init) |
| 125 | + |
| 126 | + |
0 commit comments