1. 程式人生 > >python面向對象, 單例模式

python面向對象, 單例模式

++ som 是否 直接 log 周期 字符 def 控制

@(python之路)[面向對象, 單例模式]

[TOC]

單利模式

單例模式:是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
單例模式的特點:

  • 一、某個類只能有一個實例;
  • 二、他必須自行創建這個實例;
  • 三、它必須自行想整個系統提供這個實例;

實現單利模式的方法

  • 使用模塊
  • 使用__new__
  • 使用裝飾器(decorator)
  • 使用元類(metaclass)

使用模塊

在模塊第一次導入時,會生成.pyc文件,當第二次導入時,就會一直加載.pyc文件,而不會再一次執行本次代碼。因此,我們只獲得了一個單例模式。

例如:

#mysingleton.py
class MySingleton(object):
    def foo(self):
        print("My singleton")
mysingleton = MySingleton()
#main.py
from mysingleton import mysingleton
mysingleton.foo()

使用__new__
為了使類只能出現一個實例,我們可以使用 new 來控制實例的創建過程,代碼如下:

class Singleton(object):
    _instance = None
    def
__new__(cls, *args, **kw): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) #這裏的super的意思是,該怎麽走就怎麽走。 return cls._instance class MyClass(Singleton): a = 1

在上面的代碼中,我們將類的實例和一個類變量 _instance 關聯起來,如果 cls._instance 為 None 則創建實例,否則直接返回 cls._instance。

執行情況:

>>> one = MyClass()
>>> two = MyClass()
>>> one == two
True
>>> one is two
True
>>> id(one), id(two)
(4303862668, 4303862668)

使用裝飾器

我們知道,裝飾器(decorator)可以動態地修改一個類或函數的功能。這裏,我們也可以使用裝飾器來裝飾某個類,使其只能生成一個實例,代碼如下:

from functools import wraps
 
 
def singleton(cls):
    instances = {}
 
    @wraps(cls)
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
 
    return getinstance

@singleton
class MyClass(object):
    a = 1

在上面,我們定義了一個裝飾器 singleton,它返回了一個內部函數 getinstance,該函數會判斷某個類是否在字典 instances 中,如果不存在,則會將 cls 作為 key,cls(*args, **kw) 作為 value 存到 instances 中,否則,直接返回 instances[cls]。

使用 metaclass

元類(metaclass)可以控制類的創建過程,它主要做三件事:

  • 攔截類的創建
  • 修改類的定義
  • 返回修改後的類

用元類實現單例模式的代碼:

class Singleton(type):
    _instances = {}
 
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]
 
 
# Python2
# class MyClass(object):
#     __metaclass__ = Singleton
 
# Python3
class MyClass(metaclass=Singleton):
   pass

優點

  • 一、實例控制
    單例模式會組織其他對象實例化自己的單例對象的副本,從而確保對所有對象都訪問唯一實例。
  • 二、靈活性
    因為類控制了實例化過程,所以類可以靈活更改實例化過程。
    缺點
  • 一、開銷
    雖然開銷數量小,但是如果每次對對象請求都要檢查是否存在類的實例,仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
  • 二、可能的開發混淆
    使用單例對象(尤其是在類庫中定義的對象)時,開發人員必須記住不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己五發直接實例化此類。
  • 三、對象生存周期
    不能解決刪除對象的問題。在提供內存管理的語言中(例如基於.NET Framework的語言),只有單例類能夠導致實例被取消分配,因為它包含該實例的私有化引用。在某些語言中(如C++),其他類可以刪除對象實例,但這樣會導致實例類中出現懸浮引用。

補充:元類(metaclass)

註釋:
?類也是對象;只要你使用關鍵字class,python解釋器在執行的時候就好會創建一個對象。
例如:

class Foo(object):
    pass

這段代碼將在內存中創建於一個對象,名字為Foo。這個對象(類)自身擁有創建類對象(類實力)的能力,而這就是為什麽他是一個類的原因。但是,他本質仍然時一個對象,於是乎你可以對它的操作如下

  • 將它復制給一個變量
  • 拷貝
  • 增加它的屬性
  • 將他作為函數進行傳遞
>>> class Foo(object):
...     pass
...
>>> print(Foo)    # 打印一個類,因為他是一個對象
<class '__main__.Foo'>
##########################################
>>> def echo(o):
...     print(o)
...
>>> echo(Foo)     # 將類當作參數傳給函數
<class '__main__.Foo'>
#########################################
>>> Foo.new_attribute = "foo"   # 增加類的屬性
>>> print(hasattr(Foo,"new_attribute"))
True
>>> print(Foo.new_attribute)
foo
##########################################
MyFoo = Foo
print(MyFoo())  # 可以將類賦值給一個變量

動態地創建類
因為類也是對象,可以在運行時動態創建他們,就像其他任何對象一樣。首先,你可以在函數中創建類,使用class關鍵字即可。

def choose_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo  # 返回的是類,不是類的實例
    else:
        class Bar(object):
            pass
 
        return Bar
 
MyClass = choose_class('foo')
print(MyClass)      # 函數返回的是類,不是類的實例
# <class '__main__.choose_class.<locals>.Foo'>
 
print(MyClass())    # 你可以通過這個類創建類實例,也就是對象
# <__main__.choose_class.<locals>.Foo object at 0x00000000021E5CF8>

type動態創建類。
type可以這樣工作

type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))

例如下面的代碼:

class MyTypeClass(object):
    pass

可以用type的方式去創建:

>>> MyTypeClass = type("MyTypeClass",(),{})  # 返回一個對象
>>> print(MyTypeClass)
<class '__main__.MyTypeClass'>

>>> print(MyTypeClass())  # 創建一個類的實例
<__main__.MyTypeClass object at 0x0542A590>

type接收一個字典來為類定義屬性,因此:

class Foo(object):
    bar = True

可以翻譯為:

Foo = type("Foo",(),{"bar":True})

也可以將Foo當成一個普通類使用:

class Foo(object):
    bar = True
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>>

也可以繼承

class FooTwo(Foo):
    pass

也可以寫成:

class Foo(object):
    bar = True

FooTwo = type("FooTwo",(Foo,),{})
>>> print(FooTwo)
<class '__main__.FooTwo'>
>>> print(FooTwo.bar)     # bar屬性由Foo繼承而來
True

在python中,類也是對象,你可以動態的創建類。這就是你使用關鍵字class時python在幕後做的事情,而這就通過元類實現的。
什麽時元類,元類就是用來創建類的東西。元類就是類的類。
函數type實際上時一個元類。type就是python在背後用來創建所有類的元類。
type就是創建類對象的類,可以用__class__屬性來看到這一點。
python中所有的東西,註意這裏的東西都是對象。包括整數,字符串,函數以及類,他們全都是對象,而且他們都是一個類創建而來。

age = 35
print(age.__class__)    #<class 'int'>
 
name = 'bob'
print(name.__class__)   #<class 'str'>
 
def foo():
    pass
print(foo.__class__)    #<class 'function'>
 
class Bar(object):
    pass
b = Bar()
print(b.__class__)      #<class '__main__.Bar'>

現在,對於任何一個__class__的__class__屬性又是什麽呢。

print(age.__class__.__class__)  #<class 'type'>
print(name.__class__.__class__) #<class 'type'>
print(foo.__class__.__class__)  #<class 'type'>
print(b.__class__.__class__)    #<class 'type'>

因此,元類就是創建類這種對象的東西。
type就是python得內建元類,當然你可以創建自己的元類。
__metaclass__屬性
你可以在寫一個類得時候為其添加__metaclass__屬性。

class Foo(object):
    __metaclass__ = something...

Python會在類的定義中尋找__metaclass__屬性,如果找到了,Python就會用它來創建類Foo,如果沒有找到,就會用內建的type來創建這個類。

註:本片博客借鑒網友一片博客,地址找不到了。

python面向對象, 單例模式