单例模式是什么意思?
问题
有的时候 我们可能希望某一个对象只会在程序中出现一次,之后所有的调用使用同一个对象, 我们如何实现呢?
比如说 数据库的连接池,我需要初始化一次就可以了. 用一个对象就可以了.
如何实现无论执行多次的函数,或者类的构造方法,只会生成一个对象呢?
以下几种方式
第一种方式
- 如果是通过函数返回了一个对象,可以通过这种方式 来实现 无论函数执行多少次,都只会有一个对象生成.
from functools import update_wrapper
import redis
from config.DB import REDIS_CONFIG
def single(fn):
"""
这个函数 只会计算一次.并且对函数的参数不敏感.
single is not sensitive to
argument values, and will always return the ame value if
called with different arguments.
:param fn:
:return:
"""
name = fn.__name__
def wrapper(*args, **kw):
if name not in single.__dict__:
ret = fn(*args, **kw)
single.__dict__[name] = ret
return ret
else:
return single.__dict__[name]
return update_wrapper(wrapper, fn)
pool = redis.ConnectionPool(**REDIS_CONFIG, decode_responses=True)
@single
def get_redis_client():
client = redis.Redis(connection_pool=pool)
return client
if __name__ == '__main__':
r = get_redis_client()
r2 = get_redis_client()
r3 = get_redis_client()
print(id(r), id(r2), id(r3))
print(r == r2 == r3) # True
pass
第二种方式
直接用类装饰器来做
- 如果是类,可以通过类装饰器,来实现
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@Time : 2019/6/1 18:48
@File : mysingle3.py
@Author : frank.chang@shoufuyou.com
"""
from functools import update_wrapper
def singleton(cls):
"""
类装饰器 的使用,
这样简单的可以生成一个对象
:param cls:
:return:
"""
dic = dict()
def wrapper(*args, **kwargs):
if cls not in dic:
dic[cls] = cls(*args, **kwargs)
return dic[cls]
# copy 源数据 docstring name 等等..
return update_wrapper(wrapper, cls)
@singleton
class Person:
"""
This is Person
"""
name = "Frank"
if __name__ == '__main__':
p = Person()
p2 = Person()
p3 = Person()
print(p, p2, p3)
print(p == p2 == p3)
print(id(p), id(p2), id(p3))
pass
结果如下:
<__main__.Person object at 0x109e259e8> <__main__.Person object at 0x109e259e8> <__main__.Person object at 0x109e259e8>
True
4460796392 4460796392 4460796392
可以看出 是一个对象,id 是一样的.
第三种方式
- 实现 单例模式
这种方式 算是 比较标准的单例模式, 直接实现 __new__ 方法 ,同时定义一个类变量保存这个值,如果没有第一次 先生成好,赋值给类变量,之后发布会这个 类变量即可.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@Time : 2019/5/30 11:01
@File : redis_single.py
@Author : frank.chang@shoufuyou.com
"""
import redis
from config.DB import REDIS_CONFIG
class RedisClient:
"""
单例模式
"""
_instance = None
@classmethod
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = redis.StrictRedis(**REDIS_CONFIG, db=0)
return cls._instance
if __name__ == '__main__':
# redis 客户端初始化工作
r = RedisClient()
r2 = RedisClient()
r3 = RedisClient()
print(id(r), id(r2), id(r3))
print(r == r2 == r3)
pass
第四种方式 meta
使用元类来搞事情, 通过生成类的时候 实现call 魔术方法 来完成 类创建实例.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@Time : 2019/6/1 18:48
@File : mysingle4.py
@Author : frank.chang@shoufuyou.com
"""
class SingletonMeta(type):
def __new__(cls, clsname, bases, clsdict):
clsobj = super().__new__(cls, clsname, bases, clsdict)
clsobj._instance = None
return clsobj
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
class Person(metaclass=SingletonMeta):
def __init__(self, name='frank'):
self.name = name
def __str__(self):
return "%s(name=%r)" % (self.__class__.__name__, self.name)
if __name__ == '__main__':
p = Person()
p2 = Person()
p3 = Person()
print(p == p2 == p3) # True
print(id(p), id(p2), id(p3))
pass
元类也可以直接在 clsdict 添加这个属性设置None, 其实是和上面是一样的.
class SingletonMeta(type):
def __new__(cls, clsname, bases, clsdict):
# 动态 添加类 的变量
clsdict["_instance"] = None
return super().__new__(cls, clsname, bases, clsdict)
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
总结
简单总结如何在python 中生成 一个对象的方法.通过 装饰器,类装饰器,通过 __new__ 方法 ,元类等 来实现只生成一个对象. 当然 可能有很多的方法,根据自己的需求选一种就可以了.
<center> 分享快乐,留住感动. '2019-06-01 19:40:34' --frank </center>文档分享:
博客-单例模式是什么