设计模式03装饰者模式

python对装饰器支持的非常好, 但是常见的python装饰器并不代表整个装饰者模式,它只是装饰者模式的一种实现的表现,模式一般试图在宏观上定义它的思想。下面列出Python装饰器的几种写法(这些代码是我一位朋友收集的,经过他同意我拿过来进行加工,这是他的博客

函数装饰器

1. 函数不带参数
# -.- coding:utf-8 -.-


def decorator(fun):

    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world()


# 运行结果
装饰器在这里动态的增加一条打印记录.
hello world!
装饰器内部原理

要点:
装饰器会在对象加载的时候就执行, 而不是在执行阶段才执行。为了更直观体现, 这里在加一个代码片段。

# -.- coding:utf-8 -.-


def decorator(fun):

    print('这里是加载和检查阶段: 测试装饰器是在执行阶段之前就已经运行')

    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


print('这里是还没检查到decorator是一个装饰器: 1')
@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str
print('这里是已经检查到decorator是一个装饰器: 2')

if __name__ == '__main__':
    # 取消执行这行代码
    # hello_world()

    print('这里是执行阶段')

# 运行结果
这里是还没检查到decorator是一个装饰器: 1
这里是加载和检查阶段: 测试装饰器是在执行阶段之前就已经运行
这里已经检查到decorator是一个装饰器: 2
这里是执行阶段

备注:
仔细看上面的思维脑图会发现加载了hello_world对象, 但是这个对象是实际上是一个decorator.<local>.wrapper这种东西,让它看起来正常一些,可以使用functools.wrap,这个不是很重要,我支持做个备注。

# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


@decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str


if __name__ == '__main__':
    # 没有加functools.wrap之前下面这行显示出来的结果是: wrapper
    # print(hello_world.__name__)

    # 加了functools.wrap之后下面这行显示出来的结果是: hello_world
    print(hello_world.__name__)
    

# 运行结果
hello_world
2. 类不带参数
# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper():
        print('装饰器在这里动态的增加一条打印记录.')
        return fun()

    return wrapper


@decorator
class HelloWorld(object):

    def __init__(self):
        self.message = 'hello world!'
        print(self.message)

if __name__ == '__main__':
    HelloWorld()


# 运行结果
装饰器在这里动态的增加一条打印记录.
hello world!
3. 函数带参数
# -.- coding:utf-8 -.-


def decorator(fun):

    def wrapper(*args, **kwargs):                   # add here
        print('装饰器在这里动态的增加一条打印记录.')
        return fun(*args, **kwargs)                 # add here

    return wrapper


@decorator
def hello_world(introduce):                         # add here
    _str = 'hello world!\n{}'.format(introduce)     # invoke here       
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world("I'm Learning Python.")


# 运行结果
装饰器在这里动态的增加一条打印记录.
hello world!
I'm Learning Python.
4. 类带参数
# -.- coding:utf-8 -.-
import functools


def decorator(fun):

    @functools.wraps(fun)
    def wrapper(*args, **kwargs):           # 调整这里
        print('装饰器在这里动态的增加一条打印记录.')
        return fun(*args, **kwargs)         # 调整这里

    return wrapper


@decorator
class HelloWorld(object):

    def __init__(self, message):            # 调整这里
        self.message = message              # 调整这里
        print(self.message) 

if __name__ == '__main__':
    HelloWorld('hello world!')           # 调整这里

5. 装饰器带参数
# -.- coding:utf-8 -.-


def decorator(status_code, msg=None):
    """
    
    :param status_code: 当传过来的状态码是400-500区间时, 
                         就抛出异常(不执行装饰对象行数). 
    :param msg: 抛异常时显示的信息.
    :return: 
    """
    def wrapper_(fun):
        def wrapper__(*args, **kwargs):
            if 400 <= status_code <= 500:
                raise RuntimeError(msg)
            fun(*args, **kwargs)
        return wrapper__

    return wrapper_


@decorator(401, '没有权限访问')
def hello_world(name, age, message='Learning python'):
    print('information: ', name, age, message)


if __name__ == '__main__':
    hello_world('zt', '18', '学习Python')


# 运行结果
Traceback (most recent call last):
  File "C:/Users/xx/_03_decorator_with_arguments.py", line 29, in <module>
    hello_world('zt', '18', '学习Python')
  File "C:/Users/xx/_03_decorator_with_arguments.py", line 16, in wrapper__
    raise RuntimeError(msg)
RuntimeError: 没有权限访问

 
 

类装饰器

函数不带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self):
        self.func()


@Decorator
def hello_world():
    _str = 'hello world!'
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world()
    
    
# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!

备注:
还是语法原则,当检查到@符号声明装饰器时,即便不运行任何代码,python也会去执行这个装饰器,并将这个hello_world当做参数传递给这个装饰器。

类不带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self):
        self.func()


@Decorator
class HelloWorld(object):

    def __init__(self):
        self.message = 'hello world!'
        print(self.message)

if __name__ == '__main__':
    HelloWorld()


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!
函数带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self, *args, **kwargs):        # 调整这里
        self.func(*args, **kwargs)              # 调整这里


@Decorator
def hello_world(message):                       # 调整这里
    _str = message                              # 调整这里
    print(_str)
    return _str


if __name__ == '__main__':
    hello_world('hello world!')


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!
类带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, func):
        print('类装饰器在这里动态的增加一条打印记录.')
        self.func = func

    def __call__(self, *args, **kwargs):            # 调整这里
        self.func(*args, **kwargs)                   # 调整这里


@Decorator
class HelloWorld(object):

    def __init__(self, message):                    # 调整这里
        self.message = message                      # 调整这里
        print(self.message)

if __name__ == '__main__':
    HelloWorld('hello world!')


# 运行结果
类装饰器在这里动态的增加一条打印记录.
hello world!
类装饰器带参数
# coding: utf-8


class Decorator(object):

    def __init__(self, status_code, msg=None):
        print('类装饰器在这里动态的增加一条打印记录.')
        if 400 <= status_code <= 500:
            raise RuntimeError(msg)

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper


@Decorator(status_code=401, msg='没有权限访问')
class HelloWorld(object):

    def __init__(self, message):
        self.message = message
        print(self.message)

if __name__ == '__main__':
    HelloWorld(message='hello world!')


# 运行结果
类装饰器在这里动态的增加一条打印记录.
Traceback (most recent call last):
  File "C:/Users/xx/_10_classdecoratorwithargument_class_with_argument.py", line 17, in <module>
    @Decorator(status_code=401, msg='没有权限访问')
  File "C:/Users/xx/_10_classdecoratorwithargument_class_with_argument.py", line 9, in __init__
    raise RuntimeError(msg)
RuntimeError: 没有权限访问


# 将status_code参数的值改为399, 再次运行
类装饰器在这里动态的增加一条打印记录.
hello world!

小结
上面就是所有的装饰器办法,你认为函数装饰器和类装饰器能去装饰一个类中的某个方法吗? 要不自己试一下?

 
 

装饰者模式

我阅读的书籍是Head First设计模式,书上的故事场景是围绕星巴克的咖啡品种和调料来动态计算价格; 例如某种类型的咖啡底价是0.9美元,加一种调料需要相应的增加一些价格,一杯咖啡可以随意组合多种调料(也允许对某一种调料加多份),最终计算出咖啡的价格。

代码
from __future__ import print_function


class DollarToRMB(object):

    @staticmethod
    def convert(dollar):
        return dollar * 6.8


################################################################################
# Beverages                                                                    #
################################################################################
class Beverage(object):

    def __init__(self):
        self._description = '饮料'
        self._price = 0

    @property
    def description(self):
        return self._description

    @property
    def price(self):
        return DollarToRMB.convert(self._price)

    def cost(self):
        raise NotImplementedError


class HouseBlend(Beverage):

    def __init__(self):
        super(HouseBlend, self).__init__()
        self._description = "星巴克首选咖啡"
        self._price = 0.89

    def cost(self):
        return self.price


class DarkRoast(Beverage):

    def __init__(self):
        super(DarkRoast, self).__init__()
        self._description = "星巴克深度烘焙咖啡"
        self._price = .99

    def cost(self):
        return self.price


class Espresso(Beverage):

    def __init__(self):
        super(Espresso, self).__init__()
        self._description = "星巴克意式浓缩咖啡"
        self._price = 1.99

    def cost(self):
        return self.price


class Decaf(Beverage):

    def __init__(self):
        super(Decaf, self).__init__()
        self._description = "星巴克不含咖啡因的咖啡"
        self._price = 1.05

    def cost(self):
        return self.price


################################################################################
# Condiment decorators                                                         #
################################################################################
class CondimentDecorator(Beverage):

    def __init__(self, beverage):
        super(CondimentDecorator, self).__init__()
        self._description = ", 调味品"
        self._price = 0
        self.beverage = beverage

    def description(self):
        raise NotImplementedError

    def cost(self):
        raise NotImplementedError


class Milk(CondimentDecorator):

    def __init__(self, beverage):
        super(Milk, self).__init__(beverage)
        self._description = ", 牛奶"
        self._price = .10

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


class Mocha(CondimentDecorator):

    def __init__(self, beverage):
        super(Mocha, self).__init__(beverage)
        self._description = ", 摩卡"
        self._price = .20

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


class Soy(CondimentDecorator):

    def __init__(self, beverage):
        super(Soy, self).__init__(beverage)
        self._description = ", 豆奶"
        self._price = .15

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


class Whip(CondimentDecorator):

    def __init__(self, beverage):
        super(Whip, self).__init__(beverage)
        self._description = ", 奶泡"
        self._price = .10

    @property
    def description(self):
        return self.beverage.description + self._description

    def cost(self):
        return self.price + self.beverage.cost()


if __name__ == '__main__':
    beverage = Espresso()
    print('{!s:<50}{}'.format(beverage.description, str(beverage.cost())))

    beverage2 = DarkRoast()
    beverage2 = Mocha(beverage2)
    beverage2 = Mocha(beverage2)
    beverage2 = Whip(beverage2)
    print('{!s:<50}{}'.format(beverage2.description, str(beverage2.cost())))

    beverage3 = HouseBlend()
    beverage3 = Soy(beverage3)
    beverage3 = Mocha(beverage3)
    beverage3 = Whip(beverage3)
    print('{!s:<50}{}'.format(beverage3.description,  str(beverage3.cost())))


# 运行结果
星巴克意式浓缩咖啡   13.532
星巴克深度烘焙咖啡, 摩卡, 摩卡, 奶泡   10.132
星巴克首选咖啡, 豆奶, 摩卡, 奶泡    9.111999999999998

模式总结

如果采用python版本的装饰器,那么这个功能就很难写的出来,因为没有利用到继承、接口和组合的特性。
都是装饰器,在应对不同的场景是有不同的写法,像python版本的装饰器最好是用来解决单一的一次性目的(例如: 验证、条件筛选);而OOP式的装饰器就显得特别灵活,能不能写得好,写的合理不合理,特别需要个人的功底积累。

核心理念

在不改变原有代码的基础上为其封装额外的行为(wrap functionality with other functionality in order to affect outputs).

模式类型

结构模式

设计原则
  1. 类应该对扩展开放,对修改关闭。
  2. 动态计算(或者叫累计计算)。

 
 

参考

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

推荐阅读更多精彩内容

  • 要点: 函数式编程:注意不是“函数编程”,多了一个“式” 模块:如何使用模块 面向对象编程:面向对象的概念、属性、...
    victorsungo阅读 1,397评论 0 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,061评论 9 118
  • 1 场景问题# 1.1 复杂的奖金计算## 考虑这样一个实际应用:就是如何实现灵活的奖金计算。 奖金计算是相对复杂...
    七寸知架构阅读 3,798评论 4 66
  • 内置函数Python解释器内置了许多功能和类型,总是可用的。他们是按字母顺序列在这里。 abs(x)返回一个数的绝...
    uangianlap阅读 1,177评论 0 0