python面向對象, 單例模式
@(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面向對象, 單例模式