python前端之旅(一)

作為一個程序員我們都知道設計模式分為三大類吱瘩,創(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ù)為大家講解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雅任,一起剝皮案震驚了整個濱河市风范,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沪么,老刑警劉巖硼婿,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異禽车,居然都是意外死亡寇漫,警方通過查閱死者的電腦和手機刊殉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來州胳,“玉大人记焊,你說我怎么就攤上這事∷ㄗ玻” “怎么了遍膜?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓤湘。 經(jīng)常有香客問我瓢颅,道長,這世上最難降的妖魔是什么弛说? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任挽懦,我火速辦了婚禮,結(jié)果婚禮上木人,老公的妹妹穿的比我還像新娘信柿。我一直安慰自己,他們只是感情好醒第,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布渔嚷。 她就那樣靜靜地躺著,像睡著了一般稠曼。 火紅的嫁衣襯著肌膚如雪圃伶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天蒲列,我揣著相機與錄音窒朋,去河邊找鬼。 笑死蝗岖,一個胖子當著我的面吹牛侥猩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抵赢,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼欺劳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了铅鲤?” 一聲冷哼從身側(cè)響起划提,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邢享,沒想到半個月后鹏往,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡骇塘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年伊履,在試婚紗的時候發(fā)現(xiàn)自己被綠了韩容。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡唐瀑,死狀恐怖群凶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哄辣,我是刑警寧澤请梢,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站力穗,受9級特大地震影響溢陪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睛廊,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杉编。 院中可真熱鬧超全,春花似錦、人聲如沸邓馒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽光酣。三九已至疏遏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間救军,已是汗流浹背财异。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唱遭,地道東北人戳寸。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像拷泽,于是被迫代替她去往敵國和親疫鹊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容

  • http://python.jobbole.com/85231/ 關于專業(yè)技能寫完項目接著寫寫一名3年工作經(jīng)驗的J...
    燕京博士閱讀 7,582評論 1 118
  • 定義類并創(chuàng)建實例 在Python中司致,類通過 class 關鍵字定義拆吆。以 Person 為例,定義一個Person類...
    績重KF閱讀 3,954評論 0 13
  • 深秋的涼踉蹌著身體 從窗戶縫鉆進屋內(nèi) 他將偷走的月光 輕輕的撒在床上 手指冷了 腳趾冷了 心也冷了 這是一顆曾經(jīng)多...
    IC同恩閱讀 395評論 8 7
  • 《平凡的世界》中潤生他媽準備用來做鞋樣的《鋼鐵是怎樣煉成的》這本書脂矫,意外被少平收獲后枣耀,沉浸在書中忘記周圍的一...
    霜雪霏霏閱讀 394評論 1 2