一.什么是装饰器?

实际上装饰器就是个函数,这个函数可以为其他函数提供附加的功能。

装饰器在给其他函数添加功能时,不会修改原函数的源代码,不会修改原函数的调用方式。

高阶函数+函数嵌套+闭包 = 装饰器

1.1什么是高阶函数?

1.1.1函数接收的参数,包涵一个函数名。

1.1.2 函数的返回值是一个函数名。

其实这两个条件都很好满足,下面就是一个高阶函数的例子。

def test1():

print "hamasaki ayumi"

def test2(func):

return test1

下面这个代码就满足了对高阶函数所有的条件,

1.1.3 满足以上条件的任意一个条件,就可以称为高阶函数。

如果只是给一个函数添加一个功能,并且不修改原函数的代码,这一点需求,我们通过高阶函数就可以实现,下面是给函数添加一个计算执行时间并不修改函数原有代码的功能。

#!/usr/bin/python2.7

# -*- coding:utf-8 -*-

import time

def foo():

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

def test(func):

start_time = time.time()

func()

stop_time = time.time()

run_time = stop_time - start_time

print run_time

test(foo)

输出结果:

hamasaki ayumi <> 3.28 now on sale!

1.00331807137

通过上面的测试结果,可以看出,没有修改foo函数的原代码,还给函数增加了一个显示执行时间的功能。

虽然使用高阶函数给原函数foo增加了功能,但是修改了原函数的调用方式,违反了开放封闭原则。

刚刚说了高阶函数接收的参数是一个函数名,我们可以利用这个特性,给函数增加功能(在不修改原代码的前提下)。

那么,高阶函数的另外一个特点,返回值是一个函数,如果应用这种特性,就可以做到不改变函数的调用方式了。那我们就来试一下,只通过高阶函数,是否可以完成装饰器的功能。

import time

def runtime(func):

start_time = time.time()

func()

stop_time = time.time()

run_time = stop_time - start_time

print run_time

return func

def foo():

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

foo = runtime(foo)

foo()

输出结果如下:

hamasaki ayumi <> 3.28 now on sale!

1.0050868988

hamasaki ayumi <> 3.28 now on sale!

从得到的结果来看,foo函数被多执行了一次,具体原因,来分析下。

首先调用了runtime(foo),解释器解释到func()的时候,foo函数被执行了一次,runtime函数在return的时候,将foo函数体,return给了foo变量,最后我们在调用foo()的时候,导致foo函数又被多执行了一次,这显然不是我们想要的效果。

所以说,“装饰器”所拥有的功能,单单只通过高阶函数,是无法实现的。

接下来就需要用到剩下的两个知识点了,分别是函数嵌套和函数闭包。

2.什么是函数嵌套?

说到函数嵌套,总会有人以为,在一个函数中调用了另外一个函数,就属于函数嵌套了,就像下面这个例子一样。

def f1():

print 'f1'

def f2():

print 'f2'

f1()

注意!!这并不是函数嵌套!!

真正的函数嵌套是指,在一个函数中又定义了一个函数。

def f1():

print 'f1'

def f2():

print 'f2'

print locals()

f1()

像这种在一个函数体内又创建了一个函数,这种形式才属于函数嵌套。

3.什么是闭包函数?

闭包函数,大概可以解释为,在一个内部函数中,对外部作用域(这里的外部作用域指的不是全局作用域!)的变量进行引用,函数内部就可以被理解为是闭包的,下面是一个闭包函数的例子。

a = 1

def f1():

a = 2

def f2():

print a

f2()

f1()

3.1 关于闭包函数的一些使用注意事项。

3.1.1 第一条要注意的就是!闭包函数内部,默认是不可以修改外部作用域的变量的!!(使用nonlocal关键字声明例外)

示例1:

def f1():

x = 1

def f2():

x = 2

print x

print x

f2()

print x

f1()

最后输出的结果是:

1

2

1

在闭包函数中定义的x变量,并没有影响到外部作用域的x变量。

例子2:

def foo():

a = 1

def bar():

a = a + 1

return a

return bar

f = foo()

print f()

分析一下这段代码所存在的问题,在执行foo()函数的时候,python会倒入闭包函数bar,来分析这个作用域的局部变量,其实python在内部规定了,在等号左边的变量都是局部变量,在闭包函数bar中,a赋值在等号的左边,被python认定为这个a是闭包函数bar中的局部变量,在执行print f()时,程序运行到a=a+1的时候,python会在闭包函数bar中找在等号右边的a的值,如果找不到,就报错了。(因为python之前已经把a当做bar闭包函数中的局部变量了。)

那么接下来,我们融合高阶函数+闭包+函数嵌套这三个知识点,来试试看是否可以实现装饰器的功能。

#!/usr/bin/python2.7

# -*- coding:utf-8 -*-

import time

def runtime(func):

def func_in():

start_time = time.time()

func()

stop_time = time.time()

run_time = stop_time - start_time

print run_time

return func_in

def foo():

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

foo = runtime(foo)

foo()

输出的结果:

hamasaki ayumi <> 3.28 now on sale!

1.00491380692

从结果中可以看出,这次通过上面说的三个知识点,成功初步实现了“装饰器”的功能。

虽然这个通过函数闭包,函数嵌套,高阶函数这三个函数的特性实现了类似装饰器的功能,但是,还是有个小瑕疵,我们看下上个例子的最后两行代码。

foo = runtime(foo)

foo()

如果有很多个函数都要使用前面这个“装饰器”的话,每个函数在调用之前都要重新赋值特别麻烦!

为了更完美的实现装饰器的功能,还需要引入python中的“语法糖”,也就是一个@符号。

这个“@”符号就是python装饰器的语法糖,用法就是@后面加上装饰器的名字,需要使用这个装饰器,“装饰”哪个函数,就把这个装饰器加在函数的上面就可以了。下面是装饰器语法糖的使用示例。

def runtime(func):

def func_in():

start_time = time.time()

func()

stop_time = time.time()

run_time = stop_time - start_time

print run_time

return func_in

@runtime

def foo():

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

foo()

#使用rumtime装饰器,对foo函数做“装饰”。

@runtime = foo = runtime(foo)

这两个语法意义是相同的,只不过不用在每次调用函数之前,都要对函数重新赋值。

4.装饰器的返回值。

这只是实现了基本的装饰器,看上面的例子,会发现一个问题,在执行foo函数的时候没有返回值。

现在有个需求,就是给foo函数添加个返回值,那我们就先直接在foo函数中retrun一个返回值出来,看看会有什么效果。

@runtime

def foo():

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

return "此处是foo函数的返回值"

a = foo() #将foo函数的返回值赋值给变量a,然后输出变量a的结果,看看是否能得到我们想要的返回值。

print a

输出结果如下:

hamasaki ayumi <> 3.28 now on sale!

1.00362682343

None

明明在foo函数中定义了返回值,但是执行了foo函数所获的的返回值依旧是None,这是为什么?

下面来分析一下原因。

首先,foo函数使用了之前写的runtime装饰器@runtime就相当于foo = runtime(foo),看起来运行的是foo函数但实际运行的是runtime函数,runtime函数中,内部包涵的子函数func_in(其实准确的说,最终执行的是runtime函数中的func_in子函数),没有包含任何返回值,如果想让这个foo函数能return出指定的返回值,就需要从上一层的装饰器开始下手。

import time

def runtime(func):

def func_in():

start_time = time.time()

ret = func() #注意看这里!!这里将传进来的foo函数的返回值赋给了ret变量

stop_time = time.time()

run_time = stop_time - start_time

print run_time

return ret #注意看这里!!这里将ret变量直接返回了出去

return func_in

@runtime

def foo():

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

return "此处是foo函数的返回值"

#foo = runtime(foo)

print foo()

下面是输出结果:

hamasaki ayumi <> 3.28 now on sale!

1.00427913666

此处是foo函数的返回值

现在通过修改runtime函数内部的func_in子函数的返回值,得到了想要的结果。

5.装饰器的变量

依旧用前面的例子举例,现在foo函数中定义了两个形参,需要接收两个参数才可以运行,首先,我们修改foo函数。

@runtime

def foo(name,age):

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

print "name:%s age:%s" %(name,age)

return "此处是foo函数的返回值"

下面来调用foo函数来看结果。

print foo(name='suhaozhi',age=18)

下面是输出结果:

print foo(name='suhaozhi',age=18)

TypeError: func_in() takes no arguments (2 given)

结果就是返回了一个异常。

我们来看下这个异常的内容,说的是func_in函数不需要任何参数,其实仔细想想我们执行foo函数,后 main真正执行的就是装饰器runtime内部定义的func_in子函数,所以,要给原来的函数传什么样的值,在装饰器内部也要做相应的修改。

import time

def runtime(func):

def func_in(*args,**kwargs): 《====#注意看这里!当调用foo函数时,其实时从这里开始执行。

start_time = time.time()

ret = func(*args,**kwargs) 《=====#注意看这里,此处用于接收func_in传进来的参数。

stop_time = time.time()

run_time = stop_time - start_time

print run_time

return ret

return func_in

@runtime

def foo(name,age):

time.sleep(1)

print "hamasaki ayumi <> 3.28 now on sale!"

print "name:%s age:%s" %(name,age)

return "此处是foo函数的返回值"

print foo(name='suhaozhi',age=18)

下面时输出结果。

hamasaki ayumi <> 3.28 now on sale!

name:suhaozhi age:18

1.00473690033

此处是foo函数的返回值

至于为什么要使用万能参数,就是因为装饰器要修饰的每个函数所传的变量都是不一样的,所以使用万能参数后,装饰器内部就可以接收各种不同类型不同个数的变量了。

本篇文章只是对装饰器的初步了解!关于装饰器的更多内容都在后面的文章中~未完待续~

python高阶函数闭包装饰器_5.初识python装饰器 高阶函数+闭包+函数嵌套=装饰器...相关推荐

  1. python开发讲解_Python开发系列课程(1) - 初识Python详解

    初识Python Python简介 Python是一个很棒的语言,从它诞生的那天起,一直致力于向开发者提供同时具备可读性和生产力的多范式编程语言.曾经有人Python仅仅是一门脚本语言,不适合构建大型 ...

  2. python设计思路怎么写_初中信息技术 初识Python教学设计

    案例名称 神秘的蟒蛇-初识 Python 科目 信息技术 教学对象 八年级 设计者 叶新苗 课时 1 课时 所用教材 湖北教育出版社 一.教材内容分析 <初识 Python > 是鄂教版教 ...

  3. python拿什么做可视化界面好_5大Python可视化库到底选哪个好?一篇文章搞定从选库到教学...

    最近和鲸社区的大佬们,不约而同地写起了可视化库的教程 虽然对于我们这种吃瓜群众来说是件好事,但 大概大佬的快乐往往就是那么的朴实无华且枯燥吧.害,管他呢,赶紧拿出来给大家瞅瞅. 今天提及的5个Pyth ...

  4. Python学习01、计算机基础概念、初识Python、常量,变量,类型和表达式、字符串、动态静态类型、注释

    前言:本文章主要用于个人复习,追求简洁,感谢大家的参考.交流和搬运,后续可能会继续修改和完善. 因为是个人复习,会有部分压缩和省略. 计算机基础概念 什么是计算机? 现在我们所说的计算机不光能进行算术 ...

  5. python 少儿趣味编程下载_零基础学Python编程(少儿趣味版)

    本书是一本少儿编程入门书,适合零基础的读者.本书以"派森号"飞船和西西船长等人的童话故事为载体,从头开始介绍了Python语言的基础语法.全书共有6个章节.每章都有约十个独立的内容 ...

  6. 初识Python必看基础知识~ 续(5)进阶之路~再接再厉~

    欢迎来到~ 初始Python 系列文章 "第五回",大家好呀~ 我是 清汉 不知不觉中已是Python基础系列中的第五篇文章了~ Python基础系列,每篇文章的篇幅都比较长.比较 ...

  7. Python中的装饰器、迭代器、生成器、推导式、匿名函数和高阶函数

    文章目录 装饰器 迭代器 生成器 推导式 匿名函数 高阶函数 装饰器 闭包 介绍装饰器前先了解一下闭包,在Python中,一切皆对象(Object),函数(Function)也不例外,也是一个普通的对 ...

  8. python中高阶函数和装饰器_三.Python高阶函数和装饰器

    1高阶函数 1.1 数学概念回顾下数学知识: y=f(x) 这是最开始接触的普通函数 y=g(f(x)) 这个就是我们接触到的高阶函数 在数学和计算机科学中,高阶函数至少应当是满足下面一个条件的函数: ...

  9. python装饰器详解-Python 函数装饰器

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...

最新文章

  1. ForefrontTMG关于单一的网络适配器限制
  2. 22年前被嘲养猪的北大学子,如今带领200多户住上别墅,90后研究生也跟他加入养猪行列...
  3. 一个简单的内核模块实现和使用
  4. 控制系统设计_PLC自动化控制系统设计基本原则
  5. Java笔记-jdbc传输clob到Oracle数据库
  6. wordpress 迁移网站更改域名解决图片无法显示
  7. CNN图像分类Keras代码转换pytorch思路与实现
  8. Linux信号的产生和处理
  9. Tilemill + tilestream + mapbox.js 自制地图
  10. python 转Excel二维表为一维表
  11. Godaddy域名注册详细图文教程(转)
  12. Chrome浏览器显示“Adobe flash player已过期”问题之解决
  13. 寻找 漂亮主题 桌面主题
  14. lamp环境实战操作建立完全属于自己的博客站点
  15. 一笔画:五环,python-turtle。画圆圈
  16. 网络设备流量及性能监控的实现
  17. JS VLC插件 js
  18. F - Nastya and Door
  19. 怎么退出自适应巡航_自适应巡航功能是何方神圣?“全速域自适应巡航”又有什么作用呢...
  20. Python绘制世界疫情地图

热门文章

  1. python查询sql_Python处理SQL语句(提供SQL查询平台使用)
  2. oracle创建数据库用户并授权,oracle创建数据库、表空间、用户并授权
  3. 输入一个正整数求所有素数因子_一个数如果恰好等于它的因子之和,这个数就称为完数。编写应用程序求1000以内所有的完数...
  4. mysql小计_使用SQL实现小计,合计以及排序_MySQL
  5. 【渝粤教育】国家开放大学2019年春季 0233-22T学前儿童语言教育 参考试题
  6. 计算机新入学教案,计算机应用 新教案(1-6周).doc
  7. fftw库在windows下的的编译和配置
  8. 计算机怎么录制视频教程,怎么录制视频教程?查看电脑具体录屏方法
  9. mysql数据库表复制备份_mysql数据库的备份以及表格数据之间的复制
  10. mysql自定义数据类型_MySQL中的数据类型