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