聊聊 Python 面試最常被問到的幾種設(shè)計(jì)模式(上)
點(diǎn)擊上方“AirPython”,選擇“加為星標(biāo)”
第一時(shí)間關(guān)注 Python 技術(shù)干貨!

1. 前言
在很多人的印象里,Python 作為一款動態(tài)編程語言,在日常開發(fā)中也很少涉及到設(shè)計(jì)模式
事實(shí)上,任何一個(gè)編程語言都可以使用設(shè)計(jì)模式,它可以保證代碼的規(guī)范性,只是每一種語言的實(shí)現(xiàn)方式略有不同而已
今天我們聊聊 Python 面試中,常被問到的 5 種設(shè)計(jì)模式,它們是:單例模式、工廠模式、構(gòu)建者模式、代理模式、觀察者模式
2. 單例模式
單例模式,是最簡單常用的設(shè)計(jì)模式,主要目的是保證某一個(gè)實(shí)例對象只會存在一個(gè),減少資源的消耗
Python 單例模式有很多實(shí)現(xiàn)方式,這里推薦下面 2 種
第 1 種,重寫?__new__ 方法
定義一個(gè)實(shí)例變量,在?__new__ 方法中保證這個(gè)變量僅僅初始化一次
#?單例模式
class?Singleton(object):
????_instance?=?None
????def?__new__(cls,?*args,?**kwargs):
????????if?cls._instance?is?None:
????????????cls._instance?=?object.__new__(cls,?*args,?**kwargs)
????????return?cls._instance使用方式如下:
if?__name__?==?'__main__':
????#?構(gòu)建3個(gè)實(shí)例
????instance1?=?Singleton()
????instance2?=?Singleton()
????instance3?=?Singleton()
????#?打印出實(shí)例的內(nèi)存地址,判斷是否是同一個(gè)實(shí)例
????print(id(instance1))
????print(id(instance2))
????print(id(instance3))第 2 種,閉包定義裝飾器
使用閉包的方式定義一個(gè)單例裝飾器,將類的定義隱藏到閉包函數(shù)中
def?singleton(cls):
????"""
????定義單例的裝飾器(閉包)
????:param?cls:
????:return:
????"""
????_instance?=?{}
????def?_singleton(*args,?**kargs):
????????if?cls?not?in?_instance:
????????????_instance[cls]?=?cls(*args,?**kargs)
????????return?_instance[cls]
????return?_singleton
使用上面裝飾器的類,構(gòu)建的實(shí)例都能保證單例存在
@singleton
class?Singleton(object):
????"""單例實(shí)例"""
????def?__init__(self,?arg1):
????????self.arg1?=?arg1
使用方式如下:
if?__name__?==?'__main__':
????instance1?=?Singleton("xag")
????instance2?=?Singleton("xingag")
????print(id(instance1))
????print(id(instance2))需要注意的是,上面 2 種方式創(chuàng)建的單例并不適用于多線程
要保證多線程中構(gòu)建的實(shí)例對象為單例,需要在?__new__ 函數(shù)中使用?threading.Lock()?加入同步鎖
class?Singleton(object):
????"""
????實(shí)例化一個(gè)對象
????"""
?????#?鎖
????_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使用的時(shí)候,在線程任務(wù)中實(shí)例化對象,運(yùn)行線程即可
def?task(arg):
????"""
????任務(wù)
????:param?arg:
????:return:
????"""
????instance?=?Singleton()
????print(id(instance),?'\n')
if?__name__?==?'__main__':
????#?3個(gè)線程
????for?i?in?range(3):
????????t?=?threading.Thread(target=task,?args=[i,?])
????????t.start()這樣,就保證了多線程創(chuàng)建的實(shí)例是單例存在的,不會導(dǎo)致臟數(shù)據(jù)!
3. 工廠模式
#?定義一系列水果
class?Apple(object):
????"""蘋果"""
????def?__repr__(self):
????????return?"蘋果"
class?Banana(object):
????"""香蕉"""
????def?__repr__(self):
????????return?"香蕉"
class?Orange(object):
????"""橘子"""
????def?__repr__(self):
????????return?"橘子"class?FactorySimple(object):
????"""簡單工廠模式"""
????@staticmethod
????def?get_fruit(fruit_name):
????????if?'a'?==?fruit_name:
????????????return?Apple()
????????elif?'b'?==?fruit_name:
????????????return?Banana()
????????elif?'o'?==?fruit_name:
????????????return?Orange()
????????else:
????????????return?'沒有這種水果'if?__name__?==?'__main__':
????#?分別獲取3種水果
????#?輸入?yún)?shù),通過簡單工廠,返回對應(yīng)的實(shí)例
????instance_apple?=?FactorySimple.get_fruit('a')
????instance_banana?=?FactorySimple.get_fruit('b')
????instance_orange?=?FactorySimple.get_fruit('o')import?abc
from?factory.fruit?import?*
class?AbstractFactory(object):
????"""抽象工廠"""
????__metaclass__?=?abc.ABCMeta
[email protected]
????def?get_fruit(self):
????????passclass?AppleFactory(AbstractFactory):
????"""生產(chǎn)蘋果"""
????def?get_fruit(self):
????????return?Apple()
class?BananaFactory(AbstractFactory):
????"""生產(chǎn)香蕉"""
????def?get_fruit(self):
????????return?Banana()
class?OrangeFactory(AbstractFactory):
????"""生產(chǎn)橘子"""
????def?get_fruit(self):
????????return?Orange()if?__name__?==?'__main__':
????#?每個(gè)工廠負(fù)責(zé)生產(chǎn)自己的產(chǎn)品也避免了我們在新增產(chǎn)品時(shí)需要修改工廠的代碼,而只要增加相應(yīng)的工廠即可
????instance_apple?=?AppleFactory().get_fruit()
????instance_banana?=?BananaFactory().get_fruit()
????instance_orange?=?OrangeFactory().get_fruit()
????print(instance_apple)
????print(instance_banana)
????print(instance_orange)
class?MaoXW_CC(object):
????"""川菜-毛血旺"""
????def?__str__(self):
????????return?"川菜-毛血旺"
class?XiaoCR_CC(object):
????"""川菜-小炒肉"""
????def?__str__(self):
????????return?"川菜-小炒肉"
class?MaoXW_XC(object):
????"""湘菜-毛血旺"""
????def?__str__(self):
????????return?"湘菜-毛血旺"
class?XiaoCR_XC(object):
????"""湘菜-小炒肉"""
????def?__str__(self):
????????return?"湘菜-小炒肉"class?AbstractFactory(object):
????"""
????抽象工廠
????既可以生產(chǎn)毛血旺,也可以生成小炒肉
????"""
????__metaclass__?=?abc.ABCMeta
[email protected]
????def?product_maoxw(self):
????????pass
[email protected]
????def?product_xiaocr(self):
????????pass
class?CCFactory(AbstractFactory):
????"""川菜館"""
????def?product_maoxw(self):
????????return?MaoXW_CC()
????def?product_xiaocr(self):
????????return?XiaoCR_CC()
class?XCFactory(AbstractFactory):
????"""湘菜館"""
????def?product_maoxw(self):
????????return?MaoXW_XC()
????def?product_xiaocr(self):
????????return?XiaoCR_XC()
if?__name__?==?'__main__':
????#?川菜炒兩個(gè)菜,分別是:毛血旺和小炒肉
????maoxw_cc?=?CCFactory().product_maoxw()
????xiaocr_cc?=?CCFactory().product_xiaocr()
????print(maoxw_cc,?xiaocr_cc)
????maoxw_xc?=?XCFactory().product_maoxw()
????xiaocr_xc?=?XCFactory().product_xiaocr()
????print(maoxw_xc,?xiaocr_xc)4. 最后
單例模式和工廠模式是日常使用最為頻繁的兩種設(shè)計(jì)模式,下篇文章將聊聊后面 3 種設(shè)計(jì)模式
我已經(jīng)將文中全部源碼上傳到后臺,關(guān)注公眾號后回復(fù)「?設(shè)計(jì)模式?」即可獲得全部源碼
如果你覺得文章還不錯,請大家?點(diǎn)贊、分享、留言下,因?yàn)檫@將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強(qiáng)動力!
