點擊跳轉Python筆記總目錄
本節目錄
一、內容
二、角色
三、使用場景
四、優點
五、單例模式的四種實現方式
- 1、文件導入的形式(常用)
- 2、基于類實現的單例模式
- 3、基于__new__實現的單例模式(最常用
- 4、基于metaclass(元類)實現的單例模式
六、單例模式的應用(會在數據庫連接池中用到單例模式)
七、額外補充的一種用裝飾器實現的單利模式
一、內容
保證一個類只有一個實例,并提供一個訪問它的全局訪問點
二、角色
單例,即只有一個類的實例
三、使用場景
單例模式是什么?當類只有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時 比如:數據庫鏈接、Socket創建鏈接
四、優點
- 對唯一實例的受控訪問
- 對唯一實例的受控訪問
單利相當于全局變量,但防止了命名空間被污染 與單利模式功能相似的概念:全局變量、靜態變量(方法)
試問?為什么用單例模式,不用全局變量呢?
??答、全局變量可能會有名稱空間的干擾,如果有重名的可能會被覆蓋
五、單例模式的四種實現方式
python元類,1、文件導入的形式(常用)
s1.pyclass Foo(object):def test(self):print("123")v = Foo()
#v是Foo的實例s2.py
from s1 import v as v1
print(v1,id(v1)) #<s1.Foo object at 0x0000000002221710> 35788560from s1 import v as v2
print(v1,id(v2)) #<s1.Foo object at 0x0000000002221710> 35788560## 兩個的內存地址是一樣的
## 文件加載的時候,第一次導入后,再次導入時不會再重新加載。
復制代碼
2、基于類實現的單例模式
## **********************單例模式:無法支持多線程情況**************=class Singleton(object):def __init__(self):import timetime.sleep(1)@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instanceimport threadingdef task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()## ********************單例模式:支持多線程情況****************、import time
import threading
class Singleton(object):_instance_lock = threading.Lock()def __init__(self):time.sleep(1)@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock: #為了保證線程安全在內部加鎖if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)
for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)## 使用先說明,以后用單例模式,obj = Singleton.instance()
## 示例:
## obj1 = Singleton.instance()
## obj2 = Singleton.instance()
## print(obj1,obj2)
## 錯誤示例
## obj1 = Singleton()
## obj2 = Singleton()
## print(obj1,obj2)
復制代碼
3、基于__new__實現的單例模式(最常用)
class Singleton:def __new__(cls, *args, **kw):if not hasattr(cls, '_instance'):cls._instance = object.__new__(cls, *args, **kw)return cls._instanceone = Singleton()
two = Singleton()two.a = 3
print(one.a)
## 3
## one和two完全相同,可以用id(), **, is檢測
print(id(one))
## 29097904
print(id(two))
## 29097904
print(one ** two)
## True
print(one is two)復制代碼
4、基于metaclass(元類)實現的單例模式
"""
1.對象是類創建,創建對象時候類的__init__方法自動執行,對象()執行類的 __call__ 方法
2.類是type創建,創建類時候type的__init__方法自動執行,類() 執行type的 __call__方法(類的__new__方法,類的__init__方法)## 第0步: 執行type的 __init__ 方法【類是type的對象】
class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):pass## 第1步: 執行type的 __call__ 方法
## 1.1 調用 Foo類(是type的對象)的 __new__方法,用于創建對象。
## 1.2 調用 Foo類(是type的對象)的 __init__方法,用于對對象初始化。
obj = Foo()
## 第2步:執行Foo的 __call__ 方法
obj()
"""## **********=類的執行流程****************
class SingletonType(type):def __init__(self,*args,**kwargs):print(self) #會不會打印? #<class '__main__.Foo'>super(SingletonType,self).__init__(*args,**kwargs)def __call__(cls, *args, **kwargs): #cls = Fooobj = cls.__new__(cls, *args, **kwargs)obj.__init__(*args, **kwargs)return objclass Foo(metaclass=SingletonType):def __init__(self,name):self.name = namedef __new__(cls, *args, **kwargs):return object.__new__(cls, *args, **kwargs)
'''1、對象是類創建的,創建對象時類的__init__方法會自動執行,對象()執行類的__call__方法2、類是type創建的,創建類時候type類的__init__方法會自動執行,類()會先執行type的__call__方法(調用類的__new__,__init__方法)Foo 這個類是由SingletonType這個類創建的
'''
obj = Foo("hiayan")## ************第三種方式實現單例模式****************=
import threadingclass SingletonType(type):_instance_lock = threading.Lock()def __call__(cls, *args, **kwargs):if not hasattr(cls, "_instance"):with SingletonType._instance_lock:if not hasattr(cls, "_instance"):cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)return cls._instanceclass Foo(metaclass=SingletonType):def __init__(self,name):self.name = nameobj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
復制代碼
六、單例模式的應用(會在數據庫連接池中用到單例模式)
mysql 優化,pool.py
import pymysql
import threading
from DBUtils.PooledDB import PooledDBclass SingletonDBPool(object):_instance_lock = threading.Lock()def __init__(self):self.pool = PooledDB(creator=pymysql, ## 使用鏈接數據庫的模塊maxconnections=6, ## 連接池允許的最大連接數,0和None表示不限制連接數mincached=2, ## 初始化時,鏈接池中至少創建的空閑的鏈接,0表示不創建maxcached=5, ## 鏈接池中最多閑置的鏈接,0和None不限制maxshared=3,## 鏈接池中最多共享的鏈接數量,0和None表示全部共享。PS: 無用,因為pymysql和MySQLdb等模塊的 threadsafety都為1,所有值無論設置為多少,_maxcached永遠為0,所以永遠是所有鏈接都共享。blocking=True, ## 連接池中如果沒有可用連接后,是否阻塞等待。True,等待;False,不等待然后報錯maxusage=None, ## 一個鏈接最多被重復使用的次數,None表示無限制setsession=[], ## 開始會話前執行的命令列表。如:["set datestyle to ...", "set time zone ..."]ping=0,## ping MySQL服務端,檢查是否服務可用。## 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = alwayshost='127.0.0.1',port=3306,user='root',password='123',database='pooldb',charset='utf8')def __new__(cls, *args, **kwargs):if not hasattr(SingletonDBPool, "_instance"):with SingletonDBPool._instance_lock:if not hasattr(SingletonDBPool, "_instance"):SingletonDBPool._instance = object.__new__(cls, *args, **kwargs)return SingletonDBPool._instancedef connect(self):return self.pool.connection()
復制代碼
app.py
from pool import SingletonDBPooldef run():pool = SingletonDBPool()conn = pool.connect()## xxxxxxcursor = conn.cursor()cursor.execute("select * from td where id=%s", [5, ])result = cursor.fetchall() ## 獲取數據cursor.close()conn.close()if __name__ ** '__main__':run()
復制代碼
七、額外補充的一種用裝飾器實現的單例模式
#
def wrapper(cls):instance = {}def inner(*args,**kwargs):if cls not in instance:instance[cls] = cls(*args,**kwargs)return instance[cls]return inner@wrapper
class Singleton(object):def __init__(self,name,age):self.name = nameself.age = ageobj1 = Singleton('haiyan',22)
obj2 = Singleton('xx',22)
print(obj1)
print(obj2)
復制代碼