python前端之旅(一)

作为一个程序员我们都知道设计模式分为三大类,创建型设计模式,行为设计模式,结构型设计模式。
今天带大家见识一下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不一样,这种函数里面需要有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));

以上模拟了一个情景,就是年终奖的发放。。。 分为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):
    """上下文,作用就是封装策略的实现细节,用户只需要知道有哪些策略可用"""
    def __init__(self, level):
        # 初始化时传入具体的策略实例
        if level == 'A':
           self.strategy = StrategyA()
        else:
           self.strategy = StrategyB()

  def excute(self,salary):
        # 负责调用具体的策略实例的接口
        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 进行设置,促发观察对象。

感觉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: 发布 @param1 type  @param2 eventArgs(事件信息)
  publish: function(type, event) {

   // 判断观察者对象集合subscribers中存不存在type属性,不存在则表示为订阅,直接返回
   if (!this.subscribers[type]) {
      return;
   }

   // 先判断对象event中存不存在type这个属性,不存在就创建该属性
    if (!event[type])  {
        event[type] = type;
    }

   // 找到该事件对应的观察者,并发布通知
    var  listeners = this.subscribers[type],
          i,
          len = listeners.length;
    for (i = 0; i < len; i++) {
        listeners[i](event);
    }

  }
}

举个例子看一下:

// 创建一个可观察者Observable实例
var publisher = new Observable();

// 创建一个对象传入到订阅者中
var eventArgs = {message: "hello observer pattern!"};


// 创建一个订阅者
var subscriber = function(eventArgs) {
    console.log(eventArgs.message);
};

// 订阅 @param1 type  @param2 fn
publisher.subscribe("message", subscriber);

// 发布 @param1 type  @param2 eventArgs(事件信息)
publisher.publish("message", eventArgs); // "hello observer pattern!"

我们试想一下如何在python中实现像jquery里面的链式调用呢。

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)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

下面我们来看一个经典的单例模式:

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 关联起来,如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance。

我们也可以用装饰器来实现

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))

上面的打印内容:

ddd
ddd
True
True
(139659475230160, 139659475230160)

不好意思,我有需要在这里插播一段广告,因为作为一个精通前后短的人,我必须要装个逼。

Python在定义变量的时候不用指明具体的的类型,解释器会在运行的时候会自动检查变量的类型,并根据需要进行隐式的类型转化。因为Python是动态语言,所以一般情况下是不推荐进行类型转化的。比如"+"操作时,如果加号两边是数据就进行加法操作,如果两边是字符串就进行字符串连接操作,如果两边是列表就进行合并操作,甚至可以进行复数的运算。解释器会在运行时根据两边的变量的类型调用不同的内部方法。当加号两边的变量类型不一样的时候,又不能进行类型转化,就会抛出TypeError的异常。

我们在js里面相加,类型不同也会自动换,而python却不一样。用js实现单例模式可没有那么复杂

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;
    }

    // 其它内容
    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

之前在魔术方法里面讲过call 的用法,大家可以参考。

4 元类

什么是元类,之前已经提到过,这个东西在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

现在知道元类是干嘛的了把,它可以让指向该元类的类实例获取某些属性和方法,相当于修饰了该类。
cls是指的元类本身,bases是值得集成的类,name就是该类本身的类名。

好了,今天就到这里,下回继续为大家讲解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • http://python.jobbole.com/85231/ 关于专业技能写完项目接着写写一名3年工作经验的J...
    燕京博士阅读 7,545评论 1 118
  • 定义类并创建实例 在Python中,类通过 class 关键字定义。以 Person 为例,定义一个Person类...
    绩重KF阅读 3,923评论 0 13
  • 深秋的凉踉跄着身体 从窗户缝钻进屋内 他将偷走的月光 轻轻的撒在床上 手指冷了 脚趾冷了 心也冷了 这是一颗曾经多...
    IC同恩阅读 387评论 8 7
  • 《平凡的世界》中润生他妈准备用来做鞋样的《钢铁是怎样炼成的》这本书,意外被少平收获后,沉浸在书中忘记周围的一...
    霜雪霏霏阅读 391评论 1 2