<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 實現(xiàn)單例模式的五種寫法

          共 8825字,需瀏覽 18分鐘

           ·

          2022-09-06 09:01

          來源: https://www.cnblogs.com/huchong/p/8244279.html

          推薦閱讀: 終于來了, ?彭濤Python 爬蟲訓(xùn)練營 !

          單例模式(Singleton Pattern)?是一種常用的軟件設(shè)計模式,該模式的主要目的是確保某一個類只有一個實例存在。當(dāng)你希望在整個系統(tǒng)中,某個類只能出現(xiàn)一個實例時,單例對象就能派上用場。

          比如,某個服務(wù)器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內(nèi)容,也就是說,很多地方都需要創(chuàng)建 AppConfig 對象的實例,這就導(dǎo)致系統(tǒng)中存在多個 AppConfig 的實例對象,而這樣會嚴(yán)重浪費內(nèi)存資源,尤其是在配置文件內(nèi)容很多的情況下。

          事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。

          在 Python 中,我們可以用多種方法來實現(xiàn)單例模式:

          1. 使用模塊
          2. 使用裝飾器
          3. 使用類
          4. 基于?__new__?方法實現(xiàn)
          5. 基于 metaclass 方式實現(xiàn)

          下面來詳細(xì)介紹:

          使用模塊

          其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導(dǎo)入時,會生成?.pyc?文件,當(dāng)?shù)诙螌?dǎo)入時,就會直接加載?.pyc?文件,而不會再次執(zhí)行模塊代碼。

          因此,我們只需把相關(guān)的函數(shù)和數(shù)據(jù)定義在一個模塊中,就可以獲得一個單例對象了。

          如果我們真的想要一個單例類,可以考慮這樣做:

              
              class?Singleton(object):
          ????def?foo(self):
          ????????pass
          singleton?=?Singleton()

          將上面的代碼保存在文件 mysingleton.py 中,要使用時,直接在其他文件中導(dǎo)入此文件中的對象,這個對象即是單例模式的對象

              
              from?mysingleton?import?singleton

          使用裝飾器

              
              def?Singleton(cls):
          ????_instance?=?{}

          ????def?_singleton(*args,?**kargs):
          ????????if?cls?not?in?_instance:
          ????????????_instance[cls]?=?cls(*args,?**kargs)
          ????????return?_instance[cls]

          ????return?_singleton


          @Singleton
          class?A(object):
          ????a?=?1

          ????def?__init__(self,?x=0):
          ????????self.x?=?x


          a1?=?A(2)
          a2?=?A(3)

          使用類

              
              class?Singleton(object):

          ????def?__init__(self):
          ????????pass

          ????@classmethod
          ????def?instance(cls,?*args,?**kwargs):
          ????????if?not?hasattr(Singleton,?"_instance"):
          ????????????Singleton._instance?=?Singleton(*args,?**kwargs)
          ????????return?Singleton._instance

          一般情況,大家以為這樣就完成了單例模式,但是當(dāng)使用多線程時會存在問題:

              
              class?Singleton(object):

          ????def?__init__(self):
          ????????pass

          ????@classmethod
          ????def?instance(cls,?*args,?**kwargs):
          ????????if?not?hasattr(Singleton,?"_instance"):
          ????????????Singleton._instance?=?Singleton(*args,?**kwargs)
          ????????return?Singleton._instance

          import?threading

          def?task(arg):
          ????obj?=?Singleton.instance()
          ????print(obj)

          for?i?in?range(10):
          ????t?=?threading.Thread(target=task,args=[i,])
          ????t.start()

          程序執(zhí)行后,打印結(jié)果如下:

              
              <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>
          <__main__.Singleton?object?at?0x02C933D0>

          看起來也沒有問題,那是因為執(zhí)行速度過快,如果在?__init__?方法中有一些 IO 操作,就會發(fā)現(xiàn)問題了。

          下面我們通過?time.sleep?模擬,我們在上面?__init__?方法中加入以下代碼:

              
              def?__init__(self):
          ????import?time
          ????time.sleep(1)

          重新執(zhí)行程序后,結(jié)果如下:

              
              <__main__.Singleton?object?at?0x034A3410>
          <__main__.Singleton?object?at?0x034BB990>
          <__main__.Singleton?object?at?0x034BB910>
          <__main__.Singleton?object?at?0x034ADED0>
          <__main__.Singleton?object?at?0x034E6BD0>
          <__main__.Singleton?object?at?0x034E6C10>
          <__main__.Singleton?object?at?0x034E6B90>
          <__main__.Singleton?object?at?0x034BBA30>
          <__main__.Singleton?object?at?0x034F6B90>
          <__main__.Singleton?object?at?0x034E6A90>

          問題出現(xiàn)了!按照以上方式創(chuàng)建的單例,無法支持多線程。

          解決辦法:加鎖!未加鎖部分并發(fā)執(zhí)行,加鎖部分串行執(zhí)行,速度降低,但是保證了數(shù)據(jù)安全。

              
              import?time
          import?threading


          class?Singleton(object):
          ????_instance_lock?=?threading.Lock()

          ????def?__init__(self):
          ????????time.sleep(1)

          ????@classmethod
          ????def?instance(cls,?*args,?**kwargs):
          ????????with?Singleton._instance_lock:
          ????????????if?not?hasattr(Singleton,?"_instance"):
          ????????????????Singleton._instance?=?Singleton(*args,?**kwargs)
          ????????return?Singleton._instance


          def?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)

          打印結(jié)果如下:

              
              <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>
          <__main__.Singleton?object?at?0x02D6B110>

          這樣就差不多了,但是還是有一點小問題,就是當(dāng)程序執(zhí)行時,執(zhí)行了?time.sleep(20)?后,下面實例化對象時,此時已經(jīng)是單例模式了。

          但我們還是加了鎖,這樣不太好,再進(jìn)行一些優(yōu)化,把?intance?方法,改成下面這樣就行:

              
              @classmethod
          def?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._instance

          這樣,一個可以支持多線程的單例模式就完成了。+

              
              import?time
          import?threading


          class?Singleton(object):
          ????_instance_lock?=?threading.Lock()

          ????def?__init__(self):
          ????????time.sleep(1)

          ????@classmethod
          ????def?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._instance


          def?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)

          這種方式實現(xiàn)的單例模式,使用時會有限制,以后實例化必須通過?obj = Singleton.instance()

          如果用?obj = Singleton(),這種方式得到的不是單例。

          基于?__new__?方法實現(xiàn)

          通過上面例子,我們可以知道,當(dāng)我們實現(xiàn)單例時,為了保證線程安全需要在內(nèi)部加入鎖。

          我們知道,當(dāng)我們實例化一個對象時,是先執(zhí)行了類的?__new__?方法(我們沒寫時,默認(rèn)調(diào)用?object.__new__),實例化對象;然后再執(zhí)行類的?__init__?方法,對這個對象進(jìn)行初始化,所有我們可以基于這個,實現(xiàn)單例模式。

              
              import?threading


          class?Singleton(object):
          ????_instance_lock?=?threading.Lock()

          ????def?__init__(self):
          ????????pass


          ????def?__new__(cls,?*args,?**kwargs):
          ????????if?not?hasattr(Singleton,?"_instance"):
          ????????????with?Singleton._instance_lock:
          ????????????????if?not?hasattr(Singleton,?"_instance"):
          ????????????????????Singleton._instance?=?object.__new__(cls)??
          ????????return?Singleton._instance

          obj1?=?Singleton()
          obj2?=?Singleton()
          print(obj1,obj2)

          def?task(arg):
          ????obj?=?Singleton()
          ????print(obj)

          for?i?in?range(10):
          ????t?=?threading.Thread(target=task,args=[i,])
          ????t.start()

          打印結(jié)果如下:

              
              <__main__.Singleton?object?at?0x038B33D0>?<__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>
          <__main__.Singleton?object?at?0x038B33D0>

          采用這種方式的單例模式,以后實例化對象時,和平時實例化對象的方法一樣?obj = Singleton()?。

          基于 metaclass 方式實現(xiàn)

          相關(guān)知識:

          1. 類由 type 創(chuàng)建,創(chuàng)建類時,type 的?__init__?方法自動執(zhí)行,類() 執(zhí)行 type 的?__call__?方法(類的?__new__?方法,類的?__init__?方法)
          2. 對象由類創(chuàng)建,創(chuàng)建對象時,類的?__init__?方法自動執(zhí)行,對象()執(zhí)行類的?__call__?方法

          例子:

              
              class?Foo:
          ????def?__init__(self):
          ????????pass

          ????def?__call__(self,?*args,?**kwargs):
          ????????pass

          obj?=?Foo()
          #?執(zhí)行type的?__call__?方法,調(diào)用 Foo類(是type的對象)的?__new__方法,用于創(chuàng)建對象,然后調(diào)用 Foo類(是type的對象)的?__init__方法,用于對對象初始化。

          obj()????#?執(zhí)行Foo的?__call__?方法????

          元類的使用:

              
              class?SingletonType(type):
          ????def?__init__(self,*args,**kwargs):
          ????????super(SingletonType,self).__init__(*args,**kwargs)

          ????def?__call__(cls,?*args,?**kwargs):?#?這里的cls,即Foo類
          ????????print('cls',cls)
          ????????obj?=?cls.__new__(cls,*args,?**kwargs)
          ????????cls.__init__(obj,*args,?**kwargs)?#?Foo.__init__(obj)
          ????????return?obj

          class?Foo(metaclass=SingletonType):?#?指定創(chuàng)建Foo的type為SingletonType
          ????def?__init__(self,name):
          ????????self.name?=?name
          ????def?__new__(cls,?*args,?**kwargs):
          ????????return?object.__new__(cls)

          obj?=?Foo('xx')

          實現(xiàn)單例模式:

              
              import?threading

          class?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._instance

          class?Foo(metaclass=SingletonType):
          ????def?__init__(self,name):
          ????????self.name?=?name


          obj1?=?Foo('name')
          obj2?=?Foo('name')
          print(obj1,obj2)

          如果你覺得文章還不錯,請大家?點贊、分享、留言?下,因為這將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強動力!

          另外,我們還開發(fā)了一門爬蟲課,從0到1帶大家學(xué)習(xí) Python 爬蟲, 既可以做副業(yè)接單,也可以做爬蟲工程師,歡迎掃碼了解。

          具體看:終于來了, ?彭濤Python 爬蟲訓(xùn)練營 !


          長按掃碼,可以試聽,想了解爬蟲更多細(xì)節(jié)可以添加「257735


          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人看天天摸 | 免费日韩黄色电影 | 婷婷美女五月天亚洲综合网 | 就去大香蕉久久 | 天堂网在线免费 |