今天你的咖啡加糖了吗? 让我们通过一个简单的例子来引出装饰器的概念及用法。在引出装饰器之前,我们先来了解一下函数的概念。 一、函数回顾 1、在python中函数是一等公民,函数也是对象。我们可以把函数赋予变量。

def make_cofe(type):
print('获得一杯 : {}'.format(type))

get_cofe = make_cofe
get_cofe('咖啡')

####输出#####
获得一杯 : 咖啡

  

这个例子中,我们把函数make_cofe 赋予了变量 get_cofe,这样之后你调用 get_cofe,就相当于是调用函数 make_cofe()。 2、把函数当作参数,传入另一个函数中。

def make_cofe(type):
print('获得一杯 : {}'.format(type))

def shop(func,type):
func(type)

shop(make_cofe,'咖啡')

####输出####
获得一杯 : 咖啡

  

这个例子,我们把make_cofe以参数的形式传入shop中,然后调用它。 3、函数是可以嵌套的。

def shop(type):
def make_cofe(type):
print('获得一杯 : {}'.format(type))
make_cofe(type)

shop('咖啡')

#####输出####
获得一杯 : 咖啡

  

这段代码中,我们在函数shop内部定义了函数make_cofe 4、函数的返回值也可以是函数对象(闭包)。

def shop():
def make_cofe(type):
print('获得一杯 : {}'.format(type))
return make_cofe

get_cofe=shop()
get_cofe("咖啡")

####输出#####
获得一杯 : 咖啡

  

这里,函数 shop() 的返回值是函数对象 make_cofe 本身,之后,我们将其赋予变量 get_cofe,再调用 get_cofe("咖啡")。 二、装饰器 下面我们正式开始装饰器的学习。 我们先想一个问题。如果我们去咖啡店要一杯咖啡,我们应该如何实现。你也许会这么写。

def cofe():
print('咖啡', end='')

cofe()

####输出####
咖啡

  

那我们现在想来一杯加糖咖啡,我们该如何写呢?你也许会这么想,那还不简单,直接在cofe()函数里改不就好了。

def cofe():
print('加糖咖啡', end='')

cofe()

####输出####
加糖咖啡

  

那么问题来了,如果我们现在不想喝加糖咖啡了,该怎么办呢,总不能在cofe()函数里去掉吧。那如果有人想喝加糖咖啡、有人不想喝加糖咖啡如何是好,总不能写两个cofe()函数吧。 那我们带着问题看一下下面这段代码。

def add_sugar(func):
def add():
print('加糖',end='')
func()
return add

def cofe():
print('咖啡',end='')

cofe = add_sugar(cofe)

print("获得一杯",end='')
cofe()

####输出#####
获得一杯加糖咖啡

  

变量 cofe 指向了内部函数 add(),而内部函数 add() 中又会调用原函数 cofe(),因此,最后调用 cofe() 时,就会先打印‘加糖’,然后输出‘咖啡’。这里的函数 add_sugar() 就是一个装饰器,它把真正需要执行的函数cofe()包裹在其中,并且改变了它的行为,但是原函数 cofe() 不变。 下面我们来看一下更优雅的写法。

def add_sugar(func):
def add():
print('加糖',end='')
func()
return add

@add_sugar
def cofe():
print('咖啡',end='')

print("获得一杯",end='')
cofe()

#####输出#####
获得一杯加糖咖啡

  

这里的@叫做语法糖, @add_sugar就相当于前面的cofe = add_sugar(cofe)语句,只不过更加简洁。因此程序中建议用这种写法。 好了,让我们来回顾下我们的问题,如果有人想喝加糖咖啡、有人不想喝加糖咖啡如何是好。学了装饰器那不就很简单了,如果要喝加糖咖啡,我们把加糖的装饰器@add_sugar给加上不就好了,如果喝不加糖的,那就不加装饰器,这样我们就把这个问题给完美解决掉了。在不改变函数内部的前提了,给函数又添加了新的功能。 到目前为止,我们已经把最简单的装饰器学完了。下面我们在考虑一个问题,如果原函数 cofe() 中,有参数需要传递给装饰器怎么办?一个简单的办法,是可以在对应的装饰器函数 add() 上,加上相应的参数。

def add_sugar(func):
def add(type):
print('加糖',end='')
func(type)
return add

@add_sugar
def cofe(type):
print('{}咖啡'.format(type),end='')


cofe("美式")
print()
cofe("拿铁")

####输出#####
加糖美式咖啡
加糖拿铁咖啡

  

不过,新的问题来了。如果我另外还有一个函数(奶茶函数),也需要使用 add_sugar() 装饰器,但是这个新的函数有两个参数,又该怎么办呢? 通常情况下,我们会把*args和 **kwargs,作为装饰器内部函数 add() 的参数。*args和**kwargs,表示接受任意数量和类型的参数,因此加糖装饰器就可以写成下面的形式:

def add_sugar(func):
def add(*args, **kwargs):
print('加糖',end='')
func(*args, **kwargs)
return add @add_sugar
def cofe(type):
print('{}咖啡'.format(type),end='')

@add_sugar
def milk_tea(type,num):
print('{}杯{}奶茶'.format(num,type), end='')


cofe("美式")
print()
milk_tea("xx牌子","4")

####输出####
加糖美式咖啡
加糖4杯xx牌子奶茶

  

这样我们的咖啡和奶茶都可以加糖了。 前面我们讲的是函数的装饰器,下面我们来讲一下类作为装饰器。类装饰器主要依赖于函数__call__(),每当你调用一个类的实例时,函数__call__()就会被执行一次。

​class Add_sugar:
def __init__(self, func):
self.func = func
self.add_suger = "加糖"

def __call__(self, *args, **kwargs):
print(self.add_suger,end='')
return self.func(*args, **kwargs)

@Add_sugar
def cofe():
print("咖啡")

cofe()

####输出#####
加糖咖啡

  

最后如果我们的咖啡既要加糖又要加冰,那我们该如何做呢?我们定义一个加冰的装饰器就好了呀。

def add_sugar(func):
def add():
print('加糖',end='')
func()
return add

def add_ice(func):
def add():
print('加冰',end='')
func()
return add


@add_sugar
@add_ice
def cofe():
print('咖啡',end='')

cofe()

####输出####
加糖加冰咖啡

  

从此以后,我们的咖啡想加什么就加什么。 欢迎大家留言和我交流。

最新文章

  1. Array数组标准库
  2. 数据持久化之NSKeyedArchiver
  3. XAML中的特殊符号几空白字符处理
  4. Visual Stadio 2015创建WebApplication应用和运行赏析
  5. 内存数据库(sqlite)和 map数据结构 做缓存对比
  6. php配置步奏
  7. JAVA中的数据结构——集合类(序):枚举器、拷贝、集合类的排序
  8. SGU 134 Centroid
  9. fuse文件系统
  10. ajax form表单提交 input file中的文件
  11. class 添加样式,删除,开关 【选择】addClass,removeClass,toggleClass
  12. Android-----View绘制流程以及invalidate()等相关方法分析 .
  13. [图形学] 习题8.12 NLN二维线段裁剪算法实现
  14. The ways to kill Oracle session
  15. html遮罩层设置
  16. Java 后端微信支付demo
  17. OpenCV+OpenCL stereo match 代码
  18. XML就是这么简单
  19. 华莱士的 第一个python程序之(用户登录)
  20. weblogic.xml 精妙设置

热门文章

  1. Flume 常用配置项
  2. Linux ll查看文件属性详解-软硬链接详解
  3. 什么是FOC
  4. 串口1配合DMA接收不定长数据(空闲中断+DMA接收)
  5. CentOS 7网络配置
  6. 用户从控制台输入n个整数,比较并输出其中的最大值和最小值
  7. oepncv实现——图像去水印
  8. lms框架模块详解
  9. 根据某个数据 来筛选 DataTree 分支数里面的数据是否符合规则 进行筛选分支,展示
  10. 国内操作系统OS分析(下)