本篇內(nèi)容主要講解“在Python中如何實(shí)現(xiàn)單例模式”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“在Python中如何實(shí)現(xiàn)單例模式”吧!
創(chuàng)新互聯(lián)是一家成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì),提供網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),網(wǎng)站制作,建網(wǎng)站,按需定制網(wǎng)站,網(wǎng)站開(kāi)發(fā)公司,于2013年成立是互聯(lián)行業(yè)建設(shè)者,服務(wù)者。以提升客戶品牌價(jià)值為核心業(yè)務(wù),全程參與項(xiàng)目的網(wǎng)站策劃設(shè)計(jì)制作,前端開(kāi)發(fā),后臺(tái)程序制作以及后期項(xiàng)目運(yùn)營(yíng)并提出專業(yè)建議和思路。方法一:使用裝飾器實(shí)現(xiàn)單例模式。
from functools
import wraps
def singleton(cls):
"""單例類裝飾器"""
instances = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls
not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class President:
pass
擴(kuò)展:裝飾器是Python中非常有特色的語(yǔ)法,用一個(gè)函數(shù)去裝飾另一個(gè)函數(shù)或類,為其添加額外的能力。通常通過(guò)裝飾來(lái)實(shí)現(xiàn)的功能都屬橫切關(guān)注功能,也就是跟正常的業(yè)務(wù)邏輯沒(méi)有必然聯(lián)系,可以動(dòng)態(tài)添加或移除的功能。裝飾器可以為代碼提供緩存、代理、上下文環(huán)境等服務(wù),它是對(duì)設(shè)計(jì)模式中代理模式的踐行。在寫(xiě)裝飾器的時(shí)候,帶裝飾功能的函數(shù)(上面代碼中的wrapper函數(shù))通常都會(huì)用functools模塊中的wraps再加以裝飾,這個(gè)裝飾器最重要的作用是給被裝飾的類或函數(shù)動(dòng)態(tài)添加一個(gè)__wrapped__屬性,這個(gè)屬性會(huì)將被裝飾之前的類或函數(shù)保留下來(lái),這樣在我們不需要裝飾功能的時(shí)候,可以通過(guò)它來(lái)取消裝飾器,例如可以使用President = President.__wrapped__來(lái)取消對(duì)President類做的單例處理。需要提醒大家的是:上面的單例并不是線程安全的,如果要做到線程安全,需要對(duì)創(chuàng)建對(duì)象的代碼進(jìn)行加鎖的處理。在Python中可以使用threading模塊的RLock對(duì)象來(lái)提供鎖,可以使用鎖對(duì)象的acquire和release方法來(lái)實(shí)現(xiàn)加鎖和解鎖的操作。當(dāng)然,更為簡(jiǎn)便的做法是使用鎖對(duì)象的with上下文語(yǔ)法來(lái)進(jìn)行隱式的加鎖和解鎖操作。
方法二:使用元類實(shí)現(xiàn)單例模式。
class SingletonMeta(type):
"""自定義單例元類"""
def __init__(cls, *args, **kwargs):
cls.__instance =
None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance
is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
class President(metaclass=SingletonMeta):
pass
擴(kuò)展:Python是面向?qū)ο蟮木幊陶Z(yǔ)言,在面向?qū)ο蟮氖澜缰?,一切皆為?duì)象。對(duì)象是通過(guò)類來(lái)創(chuàng)建的,而類本身也是對(duì)象,類這樣的對(duì)象是通過(guò)元類來(lái)創(chuàng)建的。我們?cè)诙x類時(shí),如果沒(méi)有給一個(gè)類指定父類,那么默認(rèn)的父類是object,如果沒(méi)有給一個(gè)類指定元類,那么默認(rèn)的元類是type。通過(guò)自定義的元類,我們可以改變一個(gè)類默認(rèn)的行為,就如同上面的代碼中,我們通過(guò)元類的__call__魔術(shù)方法,改變了President類的構(gòu)造器那樣。
關(guān)于單例模式,在面試中還有可能被問(wèn)到它的應(yīng)用場(chǎng)景。通常一個(gè)對(duì)象的狀態(tài)是被其他對(duì)象共享的,就可以將其設(shè)計(jì)為單例,例如項(xiàng)目中使用的數(shù)據(jù)庫(kù)連接池對(duì)象和配置對(duì)象通常都是單例,這樣才能保證所有地方獲取到的數(shù)據(jù)庫(kù)連接和配置信息是完全一致的;而且由于對(duì)象只有唯一的實(shí)例,因此從根本上避免了重復(fù)創(chuàng)建對(duì)象造成的時(shí)間和空間上的開(kāi)銷,也避免了對(duì)資源的多重占用。再舉個(gè)例子,項(xiàng)目中的日志操作通常也會(huì)使用單例模式,這是因?yàn)楣蚕淼娜罩疚募恢碧幱诖蜷_(kāi)狀態(tài),只能有一個(gè)實(shí)例去操作它,否則在寫(xiě)入日志的時(shí)候會(huì)產(chǎn)生混亂。
題目002:不使用中間變量,交換兩個(gè)變量a和b的值。
點(diǎn)評(píng):典型的送人頭的題目,在其他編程語(yǔ)言中不使用中間變量交換兩個(gè)變量的值可以使用異或運(yùn)算,Python中還可以通過(guò)內(nèi)置的字節(jié)碼指令直接交換兩個(gè)變量的值。
方法一:
a = a ^ b
b = a ^ b
a = a ^ b
方法二:
a, b = b, a
擴(kuò)展:需要注意,a, b = b, a這種做法其實(shí)并不是元組解包,雖然很多人都這樣認(rèn)為。Python字節(jié)碼指令中有ROT_TWO指令來(lái)支持這個(gè)操作,類似的還有ROT_THREE,對(duì)于3個(gè)以上的元素,如a, b, c, d = b, c, d, a,才會(huì)用到創(chuàng)建元組和元組解包。想知道你的代碼對(duì)應(yīng)的字節(jié)碼指令,可以使用Python標(biāo)準(zhǔn)庫(kù)中dis模塊的dis函數(shù)來(lái)反匯編你的Python代碼。
題目003:寫(xiě)一個(gè)刪除列表中重復(fù)元素的函數(shù),要求去重后元素相對(duì)位置保持不變。
點(diǎn)評(píng):這個(gè)題目在初中級(jí)Python崗位面試的時(shí)候經(jīng)常出現(xiàn),題目源于《Python Cookbook》這本書(shū)第一章的第10個(gè)問(wèn)題,有很多面試題其實(shí)都是這本書(shū)上的原題,所以建議大家有時(shí)間的話好好研讀一下這本書(shū)。
def
dedup(items):
no_dup_items = []
seen =
set()
for item
in items:
if item not
in seen:
no_dup_items.append(item)
seen.add(item)
return no_dup_items
當(dāng)然,也可以像《Python Cookbook》書(shū)上的代碼那樣,把上面的函數(shù)改造成一個(gè)生成器。
def
dedup(items):
seen =
set()
for item
in items:
if item not
in seen:
yield item
seen.add(item)
擴(kuò)展:由于Python中的集合底層使用哈希存儲(chǔ),所以集合的in和not in成員運(yùn)算在性能上遠(yuǎn)遠(yuǎn)優(yōu)于列表,所以上面的代碼我們使用了集合來(lái)保存已經(jīng)出現(xiàn)過(guò)的元素。集合中的元素必須是hashable對(duì)象,因此上面的代碼在列表元素不是hashable對(duì)象時(shí)會(huì)失效,要解決這個(gè)問(wèn)題可以給函數(shù)增加一個(gè)參數(shù),該參數(shù)可以設(shè)計(jì)為返回哈希碼或hashable對(duì)象的函數(shù)。
題目004:假設(shè)你使用的是官方的CPython,說(shuō)出下面代碼的運(yùn)行結(jié)果。
點(diǎn)評(píng):下面的程序?qū)?shí)際開(kāi)發(fā)并沒(méi)有什么意義,但卻是CPython中的一個(gè)大坑,這道題旨在考察面試者對(duì)官方的Python解釋器到底了解到什么程度。
a, b, c, d =
1,
1,
1000,
1000
print(a
is b, c
is d)
def foo():
e =
1000
f =
1000
print(e
is f, e
is d)
g =
1
print(g
is a)
foo()
結(jié)果:
True False
True False
True
上面代碼中a is b的結(jié)果是True但c is d的結(jié)果是False,這一點(diǎn)的確讓人費(fèi)解。這個(gè)結(jié)果是因?yàn)镃Python出于性能優(yōu)化的考慮,把頻繁使用的整數(shù)對(duì)象用一個(gè)叫small_ints的對(duì)象池緩存起來(lái)造成的。small_ints緩存的整數(shù)值被設(shè)定為[-5, 256]這個(gè)區(qū)間,也就是說(shuō),如果使用CPython解釋器,在任何引用這些整數(shù)的地方,都不需要重新創(chuàng)建int對(duì)象,而是直接引用緩存池中的對(duì)象。如果整數(shù)不在該范圍內(nèi),那么即便兩個(gè)整數(shù)的值相同,它們也是不同的對(duì)象。
CPython底層為了進(jìn)一步提升性能還做了一個(gè)設(shè)定:對(duì)于同一個(gè)代碼塊中值不在small_ints緩存范圍之內(nèi)的整數(shù),如果同一個(gè)代碼塊中已經(jīng)存在一個(gè)值與其相同的整數(shù)對(duì)象,那么就直接引用該對(duì)象,否則創(chuàng)建新的int對(duì)象。需要大家注意的是,這條規(guī)則對(duì)數(shù)值型適用,但對(duì)字符串則需要考慮字符串的長(zhǎng)度,這一點(diǎn)可以自行證明。
擴(kuò)展:如果你用PyPy(另一種Python解釋器實(shí)現(xiàn),支持JIT,對(duì)CPython的缺點(diǎn)進(jìn)行了改良,在性能上優(yōu)于CPython,但對(duì)三方庫(kù)的支持略差)來(lái)運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn)所有的輸出都是True。
題目005:Lambda函數(shù)是什么,舉例說(shuō)明的它的應(yīng)用場(chǎng)景。
點(diǎn)評(píng):這個(gè)題目主要想考察的是Lambda函數(shù)的應(yīng)用場(chǎng)景,潛臺(tái)詞是問(wèn)你在項(xiàng)目中有沒(méi)有使用過(guò)Lambda函數(shù),具體在什么場(chǎng)景下會(huì)用到Lambda函數(shù),借此來(lái)判斷你寫(xiě)代碼的能力。因?yàn)長(zhǎng)ambda函數(shù)通常用在高階函數(shù)中,主要的作用是通過(guò)傳入或返回函數(shù)實(shí)現(xiàn)代碼的解耦合。
Lambda函數(shù)也叫匿名函數(shù),它功能簡(jiǎn)單用一行代碼就能實(shí)現(xiàn)的小型函數(shù)。Python中的Lambda函數(shù)只能寫(xiě)一個(gè)表達(dá)式,這個(gè)表達(dá)式的執(zhí)行結(jié)果就是函數(shù)的返回值,不用寫(xiě)return關(guān)鍵字。Lambda函數(shù)因?yàn)闆](méi)有名字,所以也不會(huì)跟其他函數(shù)發(fā)生命名沖突的問(wèn)題。
面試的時(shí)候有可能還會(huì)考你用Lambda函數(shù)來(lái)實(shí)現(xiàn)一些功能,也就是用一行代碼來(lái)實(shí)現(xiàn)題目要求的功能,例如:用一行代碼實(shí)現(xiàn)求階乘的函數(shù),用一行代碼實(shí)現(xiàn)求大公約數(shù)的函數(shù)等。
fac = lambda x: __import__('functools').reduce(int.__mul__, range(1, x + 1), 1)
gcd = lambda x, y: y % x and gcd(y % x, x) or x
Lambda函數(shù)其實(shí)最為主要的用途是把一個(gè)函數(shù)傳入另一個(gè)高階函數(shù)(如Python內(nèi)置的filter、map等)中來(lái)為函數(shù)做解耦合,增強(qiáng)函數(shù)的靈活性和通用性。下面的例子通過(guò)使用filter和map函數(shù),實(shí)現(xiàn)了從列表中篩選出奇數(shù)并求平方構(gòu)成新列表的操作,因?yàn)橛玫搅烁唠A函數(shù),過(guò)濾和映射數(shù)據(jù)的規(guī)則都是函數(shù)的調(diào)用者通過(guò)另外一個(gè)函數(shù)傳入的,因此這filter和map函數(shù)沒(méi)有跟特定的過(guò)濾和映射數(shù)據(jù)的規(guī)則耦合在一起。
items = [12, 5, 7, 10, 8, 19]
items = list(map(lambda x: x ** 2, filter(lambda x: x % 2, items)))
print(items) # [25, 49, 361]
當(dāng)然,用列表的生成式來(lái)實(shí)現(xiàn)上面的代碼更加簡(jiǎn)單明了,如下所示。
items = [12, 5, 7, 10, 8, 19]
items = [x ** 2 for x in items if x % 2]
print(items) # [25, 49, 361]
到此,相信大家對(duì)“在Python中如何實(shí)現(xiàn)單例模式”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
當(dāng)前文章:在Python中如何實(shí)現(xiàn)單例模式-創(chuàng)新互聯(lián)
分享鏈接:http://jinyejixie.com/article4/icjie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)公司、企業(yè)網(wǎng)站制作、商城網(wǎng)站、ChatGPT、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容