六、單例模式
擴充套件:__call__
只要它含有__call__屬性,這個物件就是callable的,物件後就可以加()進行呼叫
# 含有__call__屬性的,是可呼叫的,能夠加括號 # 反之沒有__call__屬性,就是不可呼叫的,不能加括號 print(dir(lambda x, y: x+y)) # ['__annotations__', '__call__'.....] print(callable(lambda x,y:x+y)) # True print((lambda x,y:x+y)(1,2)) # 3 class MyClass(object): pass if __name__== '__main__': a = MyClass() print(a()) # TypeError: 'MyClass' object is not callable
一、__init__() 和 __new__()
__init__():建立物件、然後分配記憶體
__new__():初始化物件(並沒有建立物件)
執行順序是:先執行new(建立物件),再執行init(初始化)
new中:繼承父類,MyClass類建立物件instance並返回,返回的物件會傳給init中的self,執行初始化。
class MyClass(object): def __init__(self): print("init is running...") # new會先建立物件、然後分配記憶體 def __new__(cls, *args, **kwargs): pass print("new is running...") instance = super().__new__(cls) # 建立物件(物件是由類建立的,所以是cls return instance if __name__ == '__main__': a = MyClass() print(type(a))""" new is running... init is running... <class '__main__.MyClass'> """
二、多例模式
建立多少個物件,就分配多少個記憶體;每個物件獨享一份記憶體
class MyClass(object): pass if __name__ == '__main__': a = MyClass() b = MyClass() print(id(a)) print(id(b))
三、單例模式
不管建立多少個物件,永遠用同一個物件,物件只建立一次。
省記憶體,提高效能
1.使用__new__實現單例模式
不管建立多少物件,最終都是同一個物件,只分配一個記憶體地址
class MyClass(object): obj = None #如果物件已經建立 就直接把建立的物件返回 #如果物件未建立,建立物件,並把物件返回 def __new__(cls, *args, **kwargs): if cls.obj is None: cls.obj = super().__new__(cls) return cls.obj
2. 使用裝飾器實現單例模式
def singleton(cls): isinstance = {} def wrapper(*args, **kwargs): if not isinstance: isinstance[cls] = cls(*args, **kwargs) return isinstance[cls] return wrapper @singleton class MyClass(object): pass if __name__ == '__main__': a = MyClass() b = MyClass() print(id(a)) print(id(b))
3.使用import實現單例模式
deno_2.py class MyClass(object): pass myclass = MyClass()
from demo_2 import myclass print(id(myclass)) # 2785165023752 from demo_2 import myclass print(id(myclass)) # 2785165023752
不管import幾次,只會在第一次分配記憶體
注意:第一次匯入模組時,python直譯器會建立pyc檔案;以後再匯入時,不會執行匯入程式碼而是直接使用pyc檔案
4.用指定的類方法實現單例模式
class MyClass(object): __instance = None def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance if __name__ == '__main__': a = MyClass.singleton() b = MyClass.singleton() c = MyClass() d = MyClass() print(id(a)) # 2514584144968 print(id(b)) # 2514584144968 print(id(c)) # 2514584145032 print(id(d)) # 2514584145096
擴充套件:python物件建立的記憶體什麼時候釋放?
python的垃圾回收機制(如上面的類建立的物件放在一個main()函式中,呼叫main函式並執行完畢後,函式內部的物件會全部銷燬)
# 實現單例 與 非單例共存 class MyClass(object): __instance = None def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance def main(): a = MyClass.singleton() b = MyClass.singleton() c = MyClass() d = MyClass() print(id(a)) # 1577432513672 print(id(b)) # 1577432513672 print(id(c)) # 1577432513736 print(id(d)) # 1577432513800 if __name__ == '__main__': main()
要儘可能少的定義全域性變數,如果不寫del 變局變數,全域性變數是不會被銷燬的,除非程式結束。
四、建立執行緒安全的單例模式
執行緒不安全:雖然使用了單例模式,但是在多執行緒的情況下,單例模式“失效”了
# 執行緒不安全 # 多併發時,多執行緒造成的非單例 import threading import time class MyClass(object): __instance = None def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance def my_obj(): # 列印 檢視建立記憶體 print(MyClass.singleton()) return MyClass.singleton() if __name__ == '__main__': # 建立10個執行緒 tasks = [threading.Thread(target=my_obj) for i in range(10)] # 同時啟動10個執行緒 for task in tasks: task.start() """ <__main__.MyClass object at 0x000002991B431608>
<__main__.MyClass object at 0x000002991B431708>
<__main__.MyClass object at 0x000002991B431588>
<__main__.MyClass object at 0x000002991B431508>
<__main__.MyClass object at 0x000002991B4316C8>
<__main__.MyClass object at 0x000002991B431748>
<__main__.MyClass object at 0x000002991B431688>
<__main__.MyClass object at 0x000002991B4315C8>
<__main__.MyClass object at 0x000002991B431548> """
如何建立執行緒安全的單例模式呢?-----建立鎖,加鎖
原理:將建立物件條件的程式碼段加鎖,鎖住後,同時只能有一個執行緒在建立物件,建立物件結束後,會自動釋放鎖。第二次再建立的時候,instance非none,就實現了單例模式了
import threading import time class MyClass(object): __instance = None # 建立鎖 __lock = threading.Lock() def __init__(self, *args, **kwargs): pass @classmethod def singleton(cls ,*args, **kwargs): with cls.__lock: # with 語句段 結束後會自動釋放鎖(unlock) if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance def my_obj(): # 列印 檢視建立記憶體 print(MyClass.singleton()) return MyClass.singleton() if __name__ == '__main__': # 建立10個執行緒 tasks = [threading.Thread(target=my_obj) for i in range(10)] # 同時啟動10個執行緒 for task in tasks: task.start() """ <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> <__main__.MyClass object at 0x000001F931AC1448> """