python闭包和装饰器

DAY 9. 闭包和装饰器

9.1 闭包

闭包就是内部函数对外部函数作用域内变量的引用

可以看出

  • 闭包是针对函数的,还有两个函数,内部函数和外部函数
  • 闭包是为了让内部函数引用外部函数作用域内的变量的

我们先写两个函数

def fun1():print("我是fun1")def fun2():print("我是fun2")

这样fun2就作为fun1的内部函数,此时在函数外部是无法调用Fun2的,因为

  1. fun2实际上相当于fun1的一个属性(方法),作用域是fun1的块作用域,全局作用域中无法找到,
  2. 函数内属性的生命周期是在函数运行期间,在fun1中只是定义了fun2,并没有调用它

为了让fun2跳出fun1的生命周期,我们需要返回fun2,这样在外部获取到的fun1的返回值就是fun2,这样调用fun1的返回值就是调用了fun2,如:

def fun1():print("我是fun1")def fun2():print("我是fun2")return fun2var = fun1()
var()
# 我是fun1
# 我是fun2

当然,这还不是一个闭包,闭包是引用了自由变量的函数,所谓自由变量可以理解为局部变量,如果fun2调用了fun1中的变量,那么fun2就是一个闭包了。如

def fun1(var1):def fun2():print(f"var1 = {var1}")return fun2var = fun1(1)
var()  # var1 = 1

闭包的作用

闭包私有化了变量,实现了数据的封装,类似于面向对象

def fun1(obj):def fun2():obj[0] += 1print(obj)return fun2if __name__ == '__main__':mylist = [i for i in range(5)]var = fun1(mylist)var()var()var()# [1, 1, 2, 3, 4]# [2, 1, 2, 3, 4]# [3, 1, 2, 3, 4]

9.2 装饰器

闭包在python中有一个重要的用途就是装饰器,装饰器接受被装饰的函数作为参数并执行一次调用,装饰器的本质还是一个闭包

def func1(func):def func2():print("func2")return func()return func2@func1
def Demo():print("Demo")if __name__ == '__main__':Demo()# func2# Demo
  • 首先,@func1是一颗语法糖,等价于func1(Demo)()
  • 外部函数必须能接收一个参数,也只能接受一个参数,如果有多个参数,必须再套一个函数,因为在使用@语法糖时,会自动把被修饰函数作为参数传递给装饰器
  • 内部函数必须返回被装饰函数的调用

运行流程:

  1. 把被修饰函数作为参数传递给装饰器,这时函数返回的是闭包函数func2
  2. 隐式地调用func2,相当于func2(),执行函数体,输出func2,这时函数返回值是func(),返回的直接是被修饰函数的调用,相当于直接执行被修饰函数,输出Demo

相当于:

def func1(func):def func2():print("func2")return func()return func2# @func1
def Demo():print("Demo")if __name__ == '__main__':# s = Demo()# 先把被修饰函数作为参数传递给修饰器,这里的s就是func2s = func1(Demo)# 调用闭包函数s()print(s)# func2# Demo# <function func1.<locals>.func2 at 0x00000117F163AD90>

9.2.1 装饰器带参数

def func1(num):def func2(func):def func3():if num >10:print("大于10")else:print("小于10")return func()return func3return func2@func1(num=12)
def Demo():print("Demo")if __name__ == '__main__':Demo()b

执行流程

  1. 将装饰器的参数传递给第一层函数,并返回第二层函数func2
  2. 将被修饰函数作为参数传递给第二层函数func2,隐式调用func2,返回闭包函数
  3. 执行闭包函数,并返回被修饰函数的调用(执行被修饰函数)

9.2.2 被修饰函数带参数

如果被修饰函数带有参数,需要把参数传递给内层闭包函数,返回被修饰函数的调用时记得加参数

def func1(func):def func2(arg):arg += 1# 记得加参数return func(arg)return func2@func1
def Demo(arg):print(arg)if __name__ == '__main__':Demo(11)  # 12

9.2.3 例

  1. 求斐波那契数列任意一项的值
import timedef code_time(func):'''修饰器,用来打印函数运行时间:param func: 被修饰函数:return: func'''start_time = time.time()def closer(*args,**kwargs):result = func(*args,**kwargs)codeTime = time.time() - start_timeprint(f"This code runs at:{codeTime}")return resultreturn closerdef _Fibonacci(n):if n <= 1:return 1else:return _Fibonacci(n-1) + _Fibonacci(n-2)@code_time
def Fibonacci(n):return _Fibonacci(n)if __name__ == '__main__':var = Fibonacci(40)print(var)# This code runs at:61.738335609436035# 165580141

发现代码效率非常低,输出第四十个值需要一分多钟,这是应为每计算一个值,需要计算前两个值,这里有很多重复的,如

            10||-----------------|9                 8
|--------|       |--------|
8        7       7        67,8被重复计算多次

所以需要把已经计算过的储存起来,计算之前先判断有没有计算过,没计算过再计算,修改程序为:

import timedef code_time(func):'''修饰器,用来打印函数运行时间:param func::return:'''start_time = time.time()def closer(*args,**kwargs):result = func(*args,**kwargs)codeTime = time.time() - start_timeprint(f"This code runs at:{codeTime}")return resultreturn closer
resultList = {0:1,1:1}
def _Fibonacci(n):if n <= 1:return 1else:if n-1 in resultList:a = resultList[n-1]else:a = _Fibonacci(n-1)resultList[n-1] = aif n-2 in resultList:b = resultList[n-2]else:b = _Fibonacci(n-2)resultList[n-2] = breturn a + b@code_time
def Fibonacci(n):return _Fibonacci(n)if __name__ == '__main__':var = Fibonacci(40)print(var)# This code runs at:0.0# 165580141

速度快了很多,但重复的代码是不能忍受的,使用修饰器重新一下:

import timedef code_time(func):start_time = time.time()def closer(*args, **kwargs):result = func(*args, **kwargs)codeTime = time.time() - start_timeprint(f"This code runs at:{codeTime}")return resultreturn closerdef modify(func):catch = {0: 1, 1: 1}def closer(*args):if args not in catch:catch[args] = func(*args)return catch[args]return closer@modify
def _Fibonacci(n):if n <= 1:return 1else:return _Fibonacci(n - 1) + _Fibonacci(n - 2)@code_time
def Fibonacci(n):return _Fibonacci(n)if __name__ == '__main__':var = Fibonacci(40)print(var)

有20节楼梯,一次可以走1,2,3,4级,总共有多少种走法

from my_python_package import code_timedef Modify(c = None):if c == None:c = {}def modify(func):catch = cdef closer(*args):if args[0] not in catch:catch[args[0]] = func(*args)return catch[args[0]]return closerreturn modify@Modify()
def _Stairs(num, steps):count = 0if num == 0:count = 1elif num > 0:for step in steps:count += _Stairs(num-step,steps)return count@code_time
def Stairs(num,steps):count = _Stairs(num,steps)return countif __name__ == '__main__':num = 20steps = [step for step in range(1,5)]count = Stairs(num, steps)print(count)# Stairs runs at: 0.0 s# 283953

9.3 总结

  • 闭包:内部函数调用了外部函数作用域内的变量

    • 针对函数
    • 要有自由变量(私有变量)
    • 要点:内部函数要跳出外部函数的生命周期,需要外部函数把他return出来
  • 装饰器:

    • 基础:闭包
    • 作用:不修改原来代码的基础上拓展原函数功能
    • 用处:修改API功能,AOP编程
    • 要点:@语法糖,函数执行顺序
  • 参考链接

Python高级编程技巧(进阶)(已完结)

Python的闭包与装饰器

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

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

相关文章

学历是铜牌,能力是银牌,人脉是金牌,思维是王牌

有人工作&#xff0c;有人上学&#xff0c;大家千万不要错过这篇文章&#xff0c;能看到这篇文章也是一种幸运&#xff0c;真的受益匪浅&#xff0c;对我有很大启迪&#xff0c;这篇文章将会改变你我的一生&#xff0c;真的太好了&#xff0c;希望与有缘人分享&#xff0c;也希…

石头剪刀布python编程_《python核心编程第二版》练习题——游戏:石头剪刀布

习题里比较有意思的一个题目&#xff0c;实现石头剪刀布这个游戏&#xff0c;起初设计的时候走弯路了(主要时被习题里那个“尽量少用if判断”给整晕了)&#xff0c;想的太复杂&#xff0c;后来发现其实非常简单&#xff0c;完全可以不写if语句。还是枚举法&#xff1a;#! /usr/…

SpringMvc面试题

f-sm-1. 讲下SpringMvc和Struts1,Struts2的比较的优势 性能上Struts1>SpringMvc>Struts2 开发速度上SpringMvc和Struts2差不多,比Struts1要高f-sm-2. 讲下SpringMvc的核心入口类是什么,Struts1,Struts2的分别是什么 SpringMvc的是DispatchServlet,Struts1的是ActionServl…

python 鸭子类型

DAY 10. 鸭子类型 这个概念来源于美国印第安纳州的诗人詹姆斯惠特科姆莱利&#xff08;James Whitcomb Riley,1849-1916&#xff09;的诗句&#xff1a;”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”…

thinkphp一句话疑难解决笔记

URL_PATHINFO_DEPR, depr表示 网页路径"分隔符",用"-", 有利于seo,注意是从 sername/index.php(开始的)/home-user-login-var-value开始的,pathinfo也支持普通的参数传值(仅仅支持参数...). 在thinkphp中,有两个地方使用depr,另一个就是tpl的文件目录组织分…

python选取特定行_pandas.DataFrame选取/排除特定行的方法

pandas.DataFrame选取特定行使用Python进行数据分析时&#xff0c;经常要使用到的一个数据结构就是pandas的DataFrame&#xff0c;如果我们想要像Excel的筛选那样&#xff0c;只要其中的一行或某几行&#xff0c;可以使用isin()方法&#xff0c;将需要的行的值以列表方式传入&a…

学校选址_洛谷U3451_带权中位数

题目描述 在一条大路一旁有许多栋楼&#xff0c;每栋楼里有许多小学生&#xff08;哈哈哈一波小学生来袭&#xff01;&#xff09;。但是这条路上没有小学&#xff01;&#xff01;&#xff01;&#xff01;所以唯恐世界不乱的牛A打算在路上&#xff08;汽车什么的都不敢来这个…

python 重载的实现(single-dispatch generic function)

DAY 11. python 重载 函数重载是指允许定义参数数量或类型不同的同名函数&#xff0c;程序在运行时会根据所传递的参数类型选择应该调用的函数 &#xff0c;但在默认情况下&#xff0c;python是不支持函数重载的&#xff0c;定义同名函数会发生覆盖 def foo(a:int):print(fin…

SQL中的多表查询,以及JOIN的顺序重要么?

说法是&#xff0c;一般来说&#xff0c;JOIN的顺序不重要&#xff0c;除非你要自己定制driving table。 示例&#xff1a; SELECT a.account_id, c.fed_id, e.fname, e.lname-> FROM account AS a INNER JOIN customer AS c-> ON a.cust_id c.cust_id-> INNER JOIN …

python可变对象 不可变对象_【Python】可变对象和不可变对象

在 Python 中一切都可以看作为对象。每个对象都有各自的 id, type 和 value。id: 当一个对象被创建后&#xff0c;它的 id 就不会在改变&#xff0c;这里的 id 其实就是对象在内存中的地址&#xff0c;可以使用 id() 去查看对象在内存中地址。type: 和 id 一样当对象呗创建之后…

MySQL 调优基础(三) Linux文件系统

Linux的文件系统有点像MySQL的存储引擎&#xff0c;它支持各种各样的文件系统。它最上层是通过 virtual files system虚拟文件系统作为一个抽象接口层来对外提供调用的。然后下层的各种文件系统实现这些调用接口就行了。 1. Linux 中的 日志文件系统和非日志文件系统 文件内容的…

python 经典类和新式类

DAY 12. python新式类和旧式类 继承自object基类的类叫做新式类&#xff0c;否则叫做旧式类&#xff0c;python3中的类默认是新式类&#xff0c;之前版本默认是旧式类 rootkail:~# python python 2.7.15 (default,Jul 28 2018,11:29:29) [GCC 8.1.0] on linux2 Type "he…

Why does pthread_cond_signal not work?【转】

转自&#xff1a;http://stackoverflow.com/questions/16819169/why-does-pthread-cond-signal-not-work# 0 down vote favorite I am currently learing all around POSIX threads (pthread). I now have created a simple program which increased a shared value by 7 until…

Android开发技术周报 Issue#72

新闻 Android N 最初预览版&#xff1a;开发者 API 和工具教程 Gradle依赖的统一管理 理解Java垃圾回收机制 浅谈 Android 编程思想和架构 由Android 65K方法数限制引发的思考 Android音频开发&#xff08;1&#xff09;&#xff1a;基础知识 Android音频开发&#xff08;…

python 单例模式的四种实现方法

DAY 13. 单例设计 13.1 什么是单例设计 一个类每次实例化返回的都是同一个对象&#xff0c;这种设计模式叫做单例设计&#xff0c;这个类叫做单例类 13.2 实现单例设计的方法 13.2.1 重写__new__() class Foo:def __new__(cls,*args, **kwargs):# 如果是第一次实例化&…

Redis3.2.5部署(单节点)

1.安装jdk1.8 [rootsht-logstash-01 ~]# cd /usr/java/ [rootsht-logstash-01 java]# wget --no-check-certificate --no-cookies --header "Cookie: oraclelicenseaccept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111…

字节跳动 设计模式 pdf_凭这份pdf我拿下了美团、字节跳动、阿里、小米等大厂的offer...

关于程序员&#xff0c;除了做项目来提高自身的技术之外&#xff0c;还有一种提升自己的专业技能就是&#xff1a;多&#xff01;看&#xff01;书&#xff01;小编整理出一篇Java进阶架构师之路的核心知识&#xff0c;同时也是面试时面试官必问的知识点&#xff0c;篇章也是包…

B. One Bomb (#363 Div.2)

B. One Bombtime limit per test1 secondmemory limit per test256 megabytesinputstandard inputoutputstandard outputYou are given a description of a depot. It is a rectangular checkered field of n  m size. Each cell in a field can be empty (".") or…

力扣交替打印FooBar

这道题要注意的是两个线程唤醒和等待的顺序&#xff0c;应为第一个线程会比第二个线程更早结束&#xff0c;所以如果第一个线程已经结束&#xff0c;而第二个线程还在等待被唤醒&#xff0c;那第二个线程会一直等待下去&#xff0c;因此第一个线程要先等待后唤醒&#xff0c;这…

项目开发容易出错情况统计

2016年11月17日 11:30:45 星期四 1.适配&#xff1a; a) APP弹窗大屏幕适配&#xff08;例如&#xff0c; 是否居中&#xff09; 2.按钮状态&#xff1a; a) 按钮点击后没有disable 如果新页面加载卡顿导致用户多次点击&#xff0c;生成多次请求 b) 按钮disable后什么时候enabl…