文档章节

设计模式03装饰者模式

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:18
字数 2443
阅读 1
收藏 0

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. 动态计算(或者叫累计计算)。

 
 

参考

本文转载自:http://www.jianshu.com/p/47d3d90076e0

共有 人打赏支持
AllenOR灵感
粉丝 11
博文 2635
码字总数 83001
作品 0
程序员
私信 提问
《PHP设计模式大全》系列分享专栏

《PHP设计模式大全》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第1/3页 php设计模式介绍之值对象模式第1/5页...

kaixin_code
11/06
0
0
JavaScript常用设计模式

设计模式 设计模式是一种在长时间的经验与错误中总结出来可服用的解决方案。 设计模式主要分为3类: 创建型设计模式:专注于处理对象的创建 Constructor构造器模式,Factory工厂模式,Singl...

a独家记忆
07/13
0
0
JavaScript 中常见设计模式整理

开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式。本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知。 JavaScript 中...

牧云云
05/18
0
0
我的Java设计模式-代理模式

写完上一篇之后有小伙伴问我有没有写过代理模式,想看看我的理解。原本我的设计模式系列是按照创建型-行为型-结构型的顺序写下去的,既然小伙伴诚心诚意了,我就大发慈悲的穿插一篇代理模式。...

Jet啟思
2017/11/29
0
0
Java 中的 GoF 设计模式(第一部分)

为了理解 GoF(Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人组)的设计模式原理和历史,我制作了一个 10 分钟的短视频。(作为 PluralSight 作者试录的) 视频:https...

王桥修道院副院长
2017/02/13
2.6K
3

没有更多内容

加载失败,请刷新页面

加载更多

Linux 设备驱动--- 阻塞型字符设备驱动 --- O_NONBLOCK --- 非阻塞标志

阻塞: 在设计简单字符驱动程序时,要注意一个重要问题. 当一个设备无法立刻满足用户的读写请求时应当如何处理? 例如:调用 read 时没有数据可读,但以后可能会有; 或者一个进程试图向设备写...

天王盖地虎626
9分钟前
0
0
作用域、作用域链精解

首先来几个名词解释: 作用域[scope]:每一个js函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供js引擎存取,[[scope]]就是对象其中一个属性。[[scope]]指的就是我...

IrisHuang
9分钟前
0
0
etcd集群备份和数据恢复

etcd是一个分布式k-v数据库,在kubernetes中使用其管理集群的元数据。这里介绍etcd集群数据的备份和数据恢复的方法和步骤。 本文来自于:https://www.maideliang.com/index.php/archives/25/...

openthings
24分钟前
1
0
「阿里面试系列」面试加分项,从JVM层面了解线程的启动和停止

文章简介 这一篇主要围绕线程状态控制相关的操作分析线程的原理,比如线程的中断,线程的通信等,内容比较多,可能会分两篇文章 阿里面试系列导读:关注我的技术公众号【架构师修炼宝典】一周...

Java架构资源分享
31分钟前
4
0
centos安装confluence全攻略

https://blog.csdn.net/qwer026/article/details/51439076

happyeveryday32
44分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部