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

一.什么是装饰器?

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

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

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

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函数的返回值

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/352391.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab 画图字体,字号的设定,图片大小和比例

figure_FontSize12; set(get(gca,XLabel),FontSize,figure_FontSize,Vertical,top); set(get(gca,YLabel),FontSize,figure_FontSize,Vertical,middle); set(findobj(FontSize,12),FontSize,figure_FontSize); %这4句是将字体大小改为8号字&#xff0c;在小图里很清晰 %set(gcf…

使用Speedment和Spring创建REST API

随着Spring Boot的第4版发布&#xff0c;为Web开发企业应用程序变得非常容易。 代表开发人员仍然需要大量时间的事情是&#xff0c;例如在Hibernate中对现有数据库进行建模&#xff0c;以获取数据的面向对象的视图。 在本教程中&#xff0c;我们将探索如何与开源一起使用开源工…

Phpstorm界面不停的indexing,不停的闪烁

选择 File->Invalidate Caches / Restart...->Invalidate and Restart&#xff0c;就行了&#xff01;转载于:https://www.cnblogs.com/php-no-2/p/9848606.html

Matlab 集群计算平台搭建

家庭云服务器高阶应用&#xff1a;通过Matlab分布式计算来演示所谓的“家庭云计算”&#xff08;非云储存&#xff09;(转)Matlab是强大的计算软件&#xff0c;这不需要过多介绍了&#xff0c;大到航天航空领域&#xff0c;小到计算方程式&#xff0c;Matlab无处不在。 像是这种…

c打印无符号整数_C语言基础知识:printf的输出格式,C/C++语言编程讲解

C语言基础知识&#xff1a;printf的输出格式printf()函数是格式输出函数&#xff0c;请求printf()打印变量的指令取决与变量的类型&#xff0e;例如&#xff0c;在打印整数是使用&#xff05;d符号&#xff0c;在打印字符是用&#xff05;c 符号&#xff0e;这些符号被称为转换…

CGI简介

原始的HTML語言是設計用來展現靜態的資料&#xff0c;它讓人使用一種簡單的語法展現出豐富的多媒體資料&#xff0c;就像廣告看板一樣。 由於WWW具有相當大的商業用途&#xff0c;因此推出後大受歡迎。可是單純的靜態展示好像缺少了什麼? 如果使用者需要的資料具有時效性&…

数据分析——pyecharts

导入类库 1 from pyecharts import Pie, Bar, Gauge, EffectScatter, WordCloud, Map, Grid, Line, Timeline 2 import random make_point&#xff1a;标注&#xff0c;类似于matplotlib的text is_stack&#xff1a;堆叠&#xff0c;将同一图表中的不同图像堆叠显示 is_label_s…

第三方软件要使用QQ邮箱进行发邮件相关设置

1、要在QQ邮箱的设置界面进行相关设置。 开启相应的服务器&#xff0c;然后获得相应的授权密码即可。 相关参考&#xff1a; http://service.mail.qq.com/cgi-bin/help?subtype1&&id28&&no1001256 2、对相应的服务器和接口进行设置 相关参考&#xff1a;…

CGI相关介绍

一、CGI 简介CGI 是 Common Gateway Interface 的 简 称 。 其 主 要 的 功 能 是 在WWW 环 境 下 &#xff0c; 藉 由 从 客 户 端 传 递 一 些 讯 息 给 WWW Server &#xff0c; 再由 WWW Server 去 启 动 所 指 定 的 程 式 码 来 完 成 特 定 的 工 作 。所 以 更 明 确 的…

mapreduce 聚合_MapReduce:处理数据密集型文本处理–局部聚合第二部分

mapreduce 聚合这篇文章继续进行有关使用MapReduce进行数据密集型处理的书中实现算法的系列文章。 第一部分可以在这里找到。 在上一篇文章中&#xff0c;我们讨论了使用本地聚合技术来减少通过网络进行混洗和传输的数据量的方法。 减少传输的数据量是提高MapReduce作业效率的主…

最常出现的字符串 Most Common Word

2018-10-26 00:32:05 问题描述&#xff1a; 问题求解&#xff1a; 方法一、Trie 最长出现的字符串&#xff0c;最容易想到的解法就是Trie树了&#xff0c;于是首先使用Trie树进行了实现&#xff0c;代码量有点大&#xff0c;当然了是可以A掉的&#xff0c;只是对于这种Easy的题…

docker启动odoo提示module没有安装_Ubuntu20.04通过docker安装微信

到目前为止&#xff0c;在ubuntu20.04上使用wechat最简单的方式不是wine&#xff0c;而是用docker。今天就传授大家一个一定可以使用的docker安装的wine版本。首先&#xff0c;安装一下docker&#xff1a;sudo apt install docker.io sudo systemctl enable --now dockersudo s…

mysql如何在一个表中插入数据的同时,更新另一个表的数据?

三种方案,你看看哪个比较适合你1,适用于学生: 写两个方法,一个新增一个更新,在新增完了以后马上去查询一下,按主键倒叙排列,取到最新插入的id,前提主键是自增的且不是uuid,然后把查到的主键返回出去作为形参让更新方法接收到,然后更新即可.2,适用于ssh框架: 写两个事务,事务的传…

Python Web初学解惑之 WSGI、flup、fastcgi、web.py的关系

首先声明这篇文章 是我从 豆瓣 上面看到的。 原文地址 http://www.douban.com/note/13508388/?start0&postok#last 看我之后 豁然开朗&#xff0c;对Web的理解有加深了一层&#xff0c;在此再感谢一下文章的作者。写这篇文章 &#xff1a;一 写下 自己的理解&#xff1…

继承Javadoc方法注释

尽管用于javadoc工具的JDK工具和实用程序页面通过实现和继承方法来描述Javadoc方法注释重用的规则&#xff0c;但是当实际上不需要使用{inheritDoc}时&#xff0c;很容易不必要地显式描述注释继承&#xff0c;因为会使用相同的注释隐式继承。 Java 8 javadoc工具页面在“ 方法公…

C++之手写strlen函数

代码&#xff1a; int strlen(const char *str){ assert(str!NULL); intlen0; while((*str)!\0) len;return len; } 这个函数实现起来较为简单&#xff0c;注意字符指针的有效性检查。 可参考&#xff1a;strlen、strcpy、strcat等字符串处理函数的实现 转载于:https://www.cnb…

mysql or优化_MySQL 语句优化

官方文档放这里&#xff0c;有什么代码先到官方文档查询&#xff1a;MySQL 8.0 Reference Manual :: 8 Optimization​dev.mysql.com优化涉及多个级别的配置&#xff0c;调整和测量性能。 根据工作角色&#xff08;开发人员&#xff0c;DBA或两者的组合&#xff09;&#xff0c…

浅谈 MySQL 的存储引擎(表类型)

什么是MySql数据库 通常意义上&#xff0c;数据库也就是数据的集合&#xff0c;具体到计算机上数据库可以是存储器上一些文件的集合或者一些内存数据的集合。 我们通常说的MySql数据库&#xff0c;sql server数据库等等其实是数据库管理系统&#xff0c;它们可以存储数据&#…

Ubuntu如何搭建Django与Flup和Nginx环境?

Ubuntu系统越来越多的用户开始使用&#xff0c;本文介绍的是搭建DjangoFlupNginx环境的过程。 首先我们必须明白这这3者在该环境下发挥的作用。 1.nginx&#xff1a;("enginex")是一个高性能的HTTP和反向代理服务器&#xff0c;作用和apache的类似。它可以处理一些静…

洛谷 1137 旅行计划

【题解】 拓扑排序DP即可。 1 #include<cstdio>2 #include<cstring>3 #include<algorithm>4 #include<vector>5 #define LL long long6 #define rg register7 #define N 2000108 using namespace std;9 int n,m,front,rear,q[N],f[N],in[N]; 10 bool v…