文档章节

设计模式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灵感
粉丝 10
博文 2634
码字总数 82983
作品 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 设计模式(14) —— 复合模式

一、复合模式 模式常一起使用,组合在一个设计解决方案中 复合模式在一个解决方案中结合两个或多个模式,能解决一般性或一系列的问题 二、示例 本次设计模式讲解中无代码示例,由于复合模式是...

磊_lei
05/26
0
0
Android 设计模式-装饰模式(Decorator Pattern)

定义 饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 UML结构图 Component:组件对象接口 Concre...

Code猎人
05/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS 7.* 配置网络

配置静态IP 进入配置文件目录 cd /etc/sysconfig/network-scripts 查找以 ifcfg-eno 开头的文件并编辑它 vi ifcfg-ens32 修改文件中的变量值 BOOTPROTO=staticONBOOT=yesIPADDR=192.168...

阿白
54分钟前
1
0
深入理解OAuth2.0协议

1. 引言 如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间。是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题。豪车一般配备两种钥匙:主钥匙和泊...

xtof
58分钟前
1
0
Linux学习-0920

3.4 usermod命令 3.5 用户密码管理 3.6 mkpasswd命令 一、usermode命令 usermode作用是用来修改用户信息。 方法: usermod 参数 username 示例1:修改用户uid usermod -u 1010 test5 示例2...

wxy丶
今天
1
0
synchronized锁对象的坑

今天本来写点其他东西,碰巧写了一下synchronized,没想到掉坑里面了,大佬别笑。 起初代码大概是这样的: package com.ripplechan.part_1_2_3;import java.util.concurrent.CountDownL...

RippleChan
今天
1
0
XAMPP环境搭建(Apache + MariaDB + PHP + Perl)

operation system:ubuntu-18.04.1 step1:download XAMPP #sudo wget https://www.apachefriends.org/xampp-files/7.2.9/xampp-linux-x64-7.2.9-0-installer.run step2:install XAMPP #sudo ......

硅谷课堂
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部