作為一個程序員我們都知道設計模式分為三大類吱瘩,創(chuàng)建型設計模式框沟,行為設計模式忘巧,結(jié)構(gòu)型設計模式玄捕。
今天帶大家見識一下python里面的行為設計模式,并與js做一些比較不瓶。
1.策略模式
import types
class StrategyExample:
def __init__(self, func=None):
self.name = 'Strategy Example 0'
if func is not None:
self.execute = types.MethodType(func,self)
def execute(self):
print(self.name)
def execute_replacement1(self):
print(self.name + ' from execute 1')
def execute_replacement2(self):
print(self.name + ' from execute 2')
if __name__ == '__main__':
strat0 = StrategyExample()
strat1 = StrategyExample(execute_replacement1)
strat1.name = 'Strategy Example 1'
strat2 = StrategyExample(execute_replacement2)
strat2.name = 'Strategy Example 2'
strat0.execute()
strat1.execute()
strat2.execute()
### OUTPUT ###
# Strategy Example 0
# Strategy Example 1 from execute 1
# Strategy Example 2 from execute 2
需要注意的是禾嫉,重新定義excute的方式,如果是前端的同學會奇怪蚊丐,為什么不這樣寫:self.execute = func,這和js不一樣熙参,這種函數(shù)里面需要有self。
既然這樣我們看下js如何寫策略模式
var strategies={
"S":function (salary) {
return salary * 4;
},
"A":function (salary) {
return salary * 3;
},
"B":function (salary) {
return salary * 2;
}
};
//具體的計算方法
var calculateBonus=function (level, salary) {
return strategies[level](salary);
};
console.log(calculateBonus('S',1000));
console.log(calculateBonus('A',4000));
以上模擬了一個情景麦备,就是年終獎的發(fā)放孽椰。昭娩。。 分為3個級別黍匾,年底4薪栏渺,3,2.锐涯。
我們可以把上面這個js代碼改造一下磕诊,寫成python的策略模式。
class Strategy(object):
"""抽象算法類"""
def bonus(self):
pass
class StrategyA(Strategy):
def bonus(self,salary):
print 'my bonus {}' .format salary*3
class StrategyB(Strategy):
def bobus(self,salary):
print 'my bonus {}' .format salary*2
class Context(object):
"""上下文纹腌,作用就是封裝策略的實現(xiàn)細節(jié)霎终,用戶只需要知道有哪些策略可用"""
def __init__(self, level):
# 初始化時傳入具體的策略實例
if level == 'A':
self.strategy = StrategyA()
else:
self.strategy = StrategyB()
def excute(self,salary):
# 負責調(diào)用具體的策略實例的接口
self.strategy.bonus(salary)
2 觀察者模式
class Subject(object):
def __init__(self):
self._observers = []
def attach(self, observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self, modifier=None):
for observer in self._observers:
if modifier != observer:
observer.update(self)
# Example usage
class Data(Subject):
def __init__(self, name=''):
Subject.__init__(self)
self.name = name
self._data = 0
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
self.notify()
class HexViewer:
def update(self, subject):
print(u'HexViewer: Subject %s has data 0x%x' %(subject.name, subject.data))
class DecimalViewer:
def update(self, subject):
print(u'DecimalViewer: Subject %s has data %d' %(subject.name, subject.data))
# Example usage...
def main():
data1 = Data('Data 1')
data2 = Data('Data 2')
view1 = DecimalViewer()
view2 = HexViewer()
data1.attach(view1)
data1.attach(view2)
data2.attach(view2)
data2.attach(view1)
print(u"Setting Data 1 = 10")
data1.data = 10
print(u"Setting Data 2 = 15")
data2.data = 15
print(u"Setting Data 1 = 3")
data1.data = 3
print(u"Setting Data 2 = 5")
data2.data = 5
print(u"Detach HexViewer from data1 and data2.")
data1.detach(view2)
data2.detach(view2)
print(u"Setting Data 1 = 10")
data1.data = 10
print(u"Setting Data 2 = 15")
data2.data = 15
if __name__ == '__main__':
main()
### OUTPUT ###
# Setting Data 1 = 10
# DecimalViewer: Subject Data 1 has data 10
# HexViewer: Subject Data 1 has data 0xa
# Setting Data 2 = 15
# HexViewer: Subject Data 2 has data 0xf
# DecimalViewer: Subject Data 2 has data 15
# Setting Data 1 = 3
# DecimalViewer: Subject Data 1 has data 3
# HexViewer: Subject Data 1 has data 0x3
# Setting Data 2 = 5
# HexViewer: Subject Data 2 has data 0x5
# DecimalViewer: Subject Data 2 has data 5
# Detach HexViewer from data1 and data2.
# Setting Data 1 = 10
# DecimalViewer: Subject Data 1 has data 10
# Setting Data 2 = 15
# DecimalViewer: Subject Data 2 has data 15
以下幾個知識需要講解:
1.self._data 是私有屬性,需要通過@property注解訪問升薯。
2.通過 @data.setter 進行設置莱褒,促發(fā)觀察對象。
感覺python里面的觀察者模式代碼很少覆劈,而且比較容易看清保礼,那我們來看看js是如何做的。
// 首先定義一個可觀察對象
var Observable = function() {
// 按類型收集訂閱對象
this.subscribers = {};
} ;
// Observable 對象原型上上的3個方法: subscribe, unsubscribe, publish
Observable.prototype = {
constructor: Observable,
// @param1 type @param2 fn
subscribe: function(type, fn) {
// 首先查看對象subscribers上是否存在type屬性
if (!this.subscribers[type]) {
this.subscribers[type] = [];
}
// 將訂閱者加入到 subscribers 中
this.subscribers[type].push(fn);
},
// unsubscribe 取消訂閱 @param1 type @param2 fn
unsubscribe: function(type, fn) {
// 先判斷subscribers中存不存在type這個屬性责语,不存在直接返回
if (!this.subscribers[type]) {
return;
}
// 存在type,將要取消訂閱的訂閱者找出炮障,從訂閱者名單中刪除掉
var listeners = this.subscribers[type],
i,
len = listeners.length;
for (i = 0; i < len; i++) {
if (listeners[i] === fn) {
// 將取消訂閱的觀察者observer移除
listeners.splice(i, 1);
return;
}
}
},
// publish: 發(fā)布 @param1 type @param2 eventArgs(事件信息)
publish: function(type, event) {
// 判斷觀察者對象集合subscribers中存不存在type屬性,不存在則表示為訂閱坤候,直接返回
if (!this.subscribers[type]) {
return;
}
// 先判斷對象event中存不存在type這個屬性胁赢,不存在就創(chuàng)建該屬性
if (!event[type]) {
event[type] = type;
}
// 找到該事件對應的觀察者,并發(fā)布通知
var listeners = this.subscribers[type],
i,
len = listeners.length;
for (i = 0; i < len; i++) {
listeners[i](event);
}
}
}
舉個例子看一下:
// 創(chuàng)建一個可觀察者Observable實例
var publisher = new Observable();
// 創(chuàng)建一個對象傳入到訂閱者中
var eventArgs = {message: "hello observer pattern!"};
// 創(chuàng)建一個訂閱者
var subscriber = function(eventArgs) {
console.log(eventArgs.message);
};
// 訂閱 @param1 type @param2 fn
publisher.subscribe("message", subscriber);
// 發(fā)布 @param1 type @param2 eventArgs(事件信息)
publisher.publish("message", eventArgs); // "hello observer pattern!"
我們試想一下如何在python中實現(xiàn)像jquery里面的鏈式調(diào)用呢白筹。
class Person(object):
def __init__(self,name,actipn,play):
self.name = name
self.action = action
self.play = play
def do_action(self):
print(self.name, self.action.name, end=' ')
self.action.change(self)
return self.action
def do_play(self):
print(self.name, self.action.name, end=' ')
self.action.change(self)
return self.action
class Action(object):
def __init__(self, name ,target = None):
self.name = name
self.target = target
def amount(self, val):
print(val, end=' ')
return self
def change(self,qq):
self.target = qq
def stop(self):
print('then stop')
return self.target
class Play(object):
def __init__(self, name):
self.name = name
self.target = ''
def play(self, val):
print(val, end=' ')
return self
def change(self, qq):
self.target = qq
def stop(self):
print('then stop')
return self.target
if __name__ == '__main__':
move = Action('move')
play = Play('qwer')
person = Person('Jack', move ,play)
aa = person.do_action().amount('5m').stop().do_play().play('ddd')
output:// Jack move 5m then stop
Jack qwer ddd
講了一個題外話智末,我們最后看一下單例模式:
3 單例模式
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在徒河。當你希望在整個系統(tǒng)中系馆,某個類只能出現(xiàn)一個實例時,單例對象就能派上用場顽照。
下面我們來看一個經(jīng)典的單例模式:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
return cls._instance
class MyClass(Singleton):
a = 1
>>> one = MyClass()
>>> two = MyClass()
>>> one == two
True
>>> one is two
True
>>> id(one), id(two)
(4303862608, 4303862608)
在上面的代碼中由蘑,我們將類的實例和一個類變量 _instance 關聯(lián)起來,如果 cls._instance 為 None 則創(chuàng)建實例代兵,否則直接返回 cls._instance尼酿。
我們也可以用裝飾器來實現(xiàn)
from functools import wraps
def singleton(cls):
instances = {}
@wraps(cls)
def getinstance(*args, **kw):
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass(object):
def __init__(self, name):
self.name = name
one = MyClass('ddd')
two = MyClass('eee')
print one.name
print two.name
print one == two
print one is two
print (id(one), id(two))
上面的打印內(nèi)容:
ddd
ddd
True
True
(139659475230160, 139659475230160)
不好意思,我有需要在這里插播一段廣告植影,因為作為一個精通前后短的人裳擎,我必須要裝個逼。
Python在定義變量的時候不用指明具體的的類型思币,解釋器會在運行的時候會自動檢查變量的類型鹿响,并根據(jù)需要進行隱式的類型轉(zhuǎn)化羡微。因為Python是動態(tài)語言,所以一般情況下是不推薦進行類型轉(zhuǎn)化的惶我。比如"+"操作時拷淘,如果加號兩邊是數(shù)據(jù)就進行加法操作,如果兩邊是字符串就進行字符串連接操作指孤,如果兩邊是列表就進行合并操作,甚至可以進行復數(shù)的運算贬堵。解釋器會在運行時根據(jù)兩邊的變量的類型調(diào)用不同的內(nèi)部方法恃轩。當加號兩邊的變量類型不一樣的時候,又不能進行類型轉(zhuǎn)化黎做,就會拋出TypeError的異常叉跛。
我們在js里面相加,類型不同也會自動換蒸殿,而python卻不一樣筷厘。用js實現(xiàn)單例模式可沒有那么復雜
var Singleton = (function () {
var instantiated;
function init() {
/*這里定義單例代碼*/
return {
publicMethod: function () {
console.log('hello world');
},
publicProperty: 'test'
};
}
return {
getInstance: function () {
if (!instantiated) {
instantiated = init();
}
return instantiated;
}
};
})();
上面運用到了閉包的知識點,其實有一個更為簡潔的方法宏所。
function Universe() {
// 判斷是否存在實例
if (typeof Universe.instance === 'object') {
return Universe.instance;
}
// 其它內(nèi)容
this.start_time = 0;
this.bang = "Big";
// 緩存
Universe.instance = this;
// 隱式返回this
}
var uni = new Universe();
var uni2 = new Universe();
console.log(uni === uni2); // true
最后我們再來看一下如何使用元類寫python的單例模式
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
之前在魔術(shù)方法里面講過call 的用法酥艳,大家可以參考。
4 元類
什么是元類爬骤,之前已經(jīng)提到過充石,這個東西在python里面有點華而不實,所以我先貼上代碼
class dog(object):
pass
class animal(object):
pass
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
print name
print(cls)
print(bases)
def log(self,aa):
print aa
attrs['func'] = log
if not attrs.has_key('key'):
attrs['key'] = 'ffff'
return type.__new__(cls, name, bases, attrs)
// python2 元類使用方法
class Model2(dog,animal):
__metaclass__ = ModelMetaclass
pass
print Model2().key
print dir(Model2())
print Model2().func('777777')
打印如下
print name ==== 'Model2'
print cls ==== <class '__main__.ModelMetaclass'>
print bases ==== (<class '__main__.dog'>, <class '__main__.animal'>)
print Model2().key ==== ffffff
print dir(Model2()) ===== [....,'func' , 'key']
print Model2().func('777777') ===== 777777
現(xiàn)在知道元類是干嘛的了把霞玄,它可以讓指向該元類的類實例獲取某些屬性和方法骤铃,相當于修飾了該類。
cls是指的元類本身坷剧,bases是值得集成的類惰爬,name就是該類本身的類名。
好了惫企,今天就到這里撕瞧,下回繼續(xù)為大家講解。