1.必备知识

#### 一 ####
def foo():
print 'foo' foo #表示是函数
foo() #表示执行foo函数 #### 二 ####
def foo():
print 'foo' foo = lambda x: x + 1 foo()
# 执行下面的lambda表达式,而不再是原来的foo函数,因为函数 foo 被重新定义了

为什么需要装饰器?

来自知乎大神的形象比方

内裤可以用来遮羞,但是到了冬天它没法为我们房等御寒,宗明的人发明了长裤,有了长裤后宝宝再也不冷了,装饰器在这里就像我们说的长裤,在不影响内裤作用下,给我们的身子提供了保暖的功效。

回到主题

装饰器本事是一个Python函数,它可以让其他函数在不需要任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续使用。概括来讲装饰器的作用就是为已经存在的对象添加额外的功能。  

先看一个简单的例子

def func():
print('i am ciri')

现有一个新需求,希望可以在 输出的内容 之前和之后加些东西:

def f1():
print('ciri is my friend')
print("i am ciri")
print('hi ciri')

f2()和f3()也有类似的需求,怎么做?再写一个logging在bar函数里?这样就造成了大量雷同的到吗为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志,日志处理完成之后,在执行真正的代码。

def outer(func):
print("ciri is my friend")
func()
print("ciri is my friend") def f1():
print('i am ciri') use_logging(f1)

逻辑上不难理解,但是折让的话,我们每次都要将一个函数作为参数转递给use_logging函数。而且这种方式破坏了原有的代码逻辑结构,之前执行业务逻辑是,执行f1(),但是现在不得不修改成use_logging(bar)。那么有没有更好的方式呢?当然是有,答案就是装饰器。

def outer(func):
def inner():
print("ciri is my friend")
r = func()
print('hi ciri')
return r
return inner() @outer
def f1():
print("i am ciri")

装饰器原理

在python中@符号具有特殊性,在python中遇到@会执行两步

  • 第一步:执行outer函数,并且将其下面的函数名当做参数——(f1 就是 func)
  • 第二步:将outer的返回值重新赋值给 函数 f1 —— f1 等于outer的返回值
def outer(func):    #第一步,f1作为outer函数的参数,此时func就是就f1函数
def inner():
print("ciri is my friend")
r = func() #对应第一步,func就是 f1 的原函数
print("hi ciri")
return r return inner
#第二步:将outer的返回值重新赋值给f1,所以 f1 等于 outer的返回值
#返回值是inner这个函数,不是执行这个函数,执行括号需要加括号inner——()
#此时f1变成了新函数——inner @outer #第一步: f1就当做outer函数的参数,f1 = func,因为f1是个函数,所以func代指f1原来的函数
def f1():
print("i am ciri") f1()

用中文描述装饰器的执行过程:

def outer(func):
def inner():
print("ciri is my friend")
r = func()
print("hi ciri")
return r
return inner @outer
def f1():
print("i am ciri") f1() """
解释器从上往下执行,把outer函数放到内存
然后遇到@,执行两步
第一步:执行outer函数,并且将其下面的函数名当做参数
此时参数func就指向了——旧f1函数
第二步:将outer的返回值重新赋值给f1
此时outer的返回值是inner函数,所以f1在内存中指向inner函数,所以f1变成了——新函数inner
放到内存中 遇到f1(),执行函数f1因为f1现在已经指向了函数inner
所以会输出
ciri is running
然后遇到func(),因为func指向旧f1函数,所以执行原f1函数的内容,输出
i am ciri
并把返回值赋值给r,r就是原函数的返回值
然后输出
hi ciri
返回原函数的返回值r,遇到return函数执行结束
"""

问答时间 

问题:被修饰的函数如果有参数呢?

#带一个参数的装饰器

def outer(func):
def inner(a1):
print("cute")
func(a1)
print("charming")
return inner @outer
def index(a1):
print("ciri")
return a1 index(1) #因为index变成了func的内存函数inner,所以假如index有参数,inner也需要有参数
#又因为func保存的是原index函数,所以也需要有参数

一个参数

#带两个参数的装饰器

def outer(func):
def inner(a1,a2):
print("cute")
func(a1,a2)
print("charming")
return inner @outer
def index(a1,a2):
print("ciri")
return a1 + a2 index(1,2)

两个参数

问题:可以装饰具有n个参数的函数的装饰器吗?

#万能参数
def f1(*args,**kwargs):
print(args)
print(kwargs) f1(1,2,3,44,k1=123,k2=423) #注必须按顺序来,不能写成f1(1,2,2,3,k1=123,423)
#也不能写成f1(k1=123,k2=423,1,2,3)

注:万能参数

#带N个参数的装饰器

def outer(func):
def inner(*args,**kwargs):
print("cute")
ret = func(*args,**kwargs)
print("charming")
print(ret)
return inner @outer
def index(a1,a2):
print("ciri")
return a1 + a2 @outer
def home(a1):
print("home")
return "home"
@outer
def show(a1,a2,a3):
print("show")
return "show" index(1,2)
#index的1,2会顺利的封装到inner函数的args里面去,
#但是index的参数是(a1,a2),就需要做一堆判断去把参数准确的传进去
#如果args里面有值,就把第一个值放到a1里面去,把第二个值放到a2里面去,值越多越麻烦
#python在这方面进行了优化,func函数也写成这样(*args,**kwargs)就会自动进行做操作
home(1)
show(1,2,3)

问题:一个函数可以被多个装饰器修饰吗?

#多个装饰器装饰同一个函数
def outer_1(func):
def inner(*args,**kwargs):
print("i love you")
ret = func(*args,**kwargs)
return ret
return inner def outer(func):
def inner(*args,**kwargs):
print("cute")
ret = func(*args,**kwargs)
print("charming")
return ret
return inner @outer_1
@outer
def index(a1,a2):
print("ciri")
return a1 + a2 index(1,2)

注:一个函数被多个装饰器修饰的原理

  

  

 

Python学习日记(七)——装饰器的更多相关文章

  1. Python学习日记(九) 装饰器函数

    1.import time a.time.time() 获取到当前的时间,返回值为浮点型 import time print(time.time()) #1565422783.6497557 b.ti ...

  2. python学习日记(装饰器的补充)

    如何返回被装饰函数的函数名及注释? 问题及实现 先看典型的装饰器: def wrapper(f):#装饰器函数,f是被装饰函数 def inner(*args,**kwargs): '''执行函数之前 ...

  3. Python学习笔记012——装饰器

    1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...

  4. Python学习笔记:装饰器

    Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...

  5. python学习之day5,装饰器,生成器,迭代器,json,pickle

    1.装饰器 import os import time def auth(type): def timeer(func): def inner(*args,**kwargs): start = tim ...

  6. python学习笔记:装饰器2

    python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 一.一般装饰函数实例: import datetime def func_name(func):#定义一个装饰函数, ...

  7. Python学习——迭代器&生成器&装饰器

    一.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素.迭代器仅 ...

  8. 从零开始的Python学习Episode 11——装饰器

    装饰器 装饰器是用来处理其他函数的函数,主要作用是在不修改原有函数的情况下添加新的功能,装饰器的返回值也是一个函数对象. 简单的装饰器 import time def show_time(f): de ...

  9. python学习笔记(五):装饰器、生成器、内置函数、json

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...

  10. Python学习系列之装饰器

    装饰器的作用 装饰器用于装饰某个函数.方法或者类,它可以让这个函数执行之前或者执行之后做一些操作 手工实现一个装饰器 def outer(some_func): #装饰器 $1 def inner() ...

随机推荐

  1. TCP协议和UDP协议的对比【转】

    原文:https://blog.csdn.net/lzj2504476514/article/details/81454754 一.TCP协议的主要特点(1)TCP是面向连接的运输层协议:(2)每一条 ...

  2. SpringCloud"灰度部署"——动态刷新网关配置

    通过Acutator和SpringCloudConfig完成"灰度部署"——动态刷新网关路由配置 先声明下,我这个可能是冒牌的灰度部署,技术有限,纯粹个人笔记分享. 前段时间接到了 ...

  3. React/Refs and this DOM

    Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素. 何时使用Refs 管理焦点,文本选择或媒体播放. 触发强制动画. 集成第三方 DOM 库. 避 ...

  4. multer实现图片上传

    multer实现图片上传: ejs代码: <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  5. 手把手教你如何用java8新特性将List中按指定属性排序,过滤重复数据

    在java中常常会遇到这样一个问题,在实际应用中,总会碰到对List排序并过滤重复的问题,如果List中放的只是简单的String类型过滤so easy,但是实际应用中并不会这么easy,往往List ...

  6. Xmind ZEN破解版来袭:如何去除水印

    Xmind ZEN是一款十分优雅地思维导图软件,但是找不到其破解版,在导出图片时就会携带上水印. image-20190110110013642.png 当然,土豪请(点击这里关闭). image-2 ...

  7. 有选择性的启用SAP UI5调试版本的源代码

    在低版本的SAP UI5应用中,我们一旦切换成调试模式,那么应用程序源代码和UI5框架程序的源代码的调试版本都会重新加载,耗时很长. 我最近发现UI5新版本1.66.1提供了选择性加载调试版本的源代码 ...

  8. EventBus使用教程

    如图准备工作: 父子(子父)组件触发 EventBus.$emit('sub') EventBus.$on('sub',()=>{ console.log(1111222232211122) } ...

  9. 后台向前台响应的json数据格式的一些问题

    最近在写后台向前台easyUI页面发送数据时遇到的一些报错. 首先easyUI内部封装了许多的方法和对象,以至于很多参数都不清楚,需要查询,其次easyUI也是有内置ajax所以从后台响应回来的数据一 ...

  10. 用arduino的uno开发板为nano板子烧写bootloader

    这篇文章,是为了记录下某宝上淘到的一个没有bootloader的nano开发板的历程(比较坑),自己搜索资料而记录的. 如果没有bootloader,板子就不能接收上传的程序,什么也干不了. 烧写bo ...