Python核心编程的四大神兽:迭代器、生成器、闭包以及装饰器
生成器
生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yield关键字时该函数会暂停执行该函数后续的代码,并且返回一个值;在下一次调用该函数执行时,程序将从上一次暂停的位置继续往下执行。
通过一个例子来理解生成器的执行过程。求1-10的所有整数的立方并将结果打印输出,正常使用列表的实现如下:
输出结果如下:
当数据量很少时,可以很快得到结果。但是如果范围扩大到10000甚至是100000000,就会发现程序执行时间会变长,变卡,甚至有可能会因超出内存空间而出现程序崩溃的现象。这是因为当数据量变得非常大的时候,内存需要开辟很大的空间去存储这些数据,内存都被吃了,自然会变慢变卡。使用生成器就能解决这个问题。
对于上述同一个问题用生成器实现如下,将范围扩大到1-10000000:
执行效果如下:

可以看到没有任何的结果输出,这说明程序已经可以顺利执行。对于迭代器来讲需要用next()方法来获取值,修改主函数为以下情况可以打印输出前4个整数的立方数:
输出结果如下:

到此可以看到,生成器生成的值需要使用next()方法一个一个的取,它不会一次性生成所有的计算结果,只有在取值时才调用,这时程序会返回计算的一个值且程序暂停;下一次取值时从上一次中断了的地方继续往下执行。
以取出前3个值为例,下图为生成器代码解析图:

图解:Python解释器从上往下解释代码,首先是函数定义,这时在计算机内存开辟了一片空间来存储这个函数,函数没有被执行,继续往下解释;到了主函数部分,首先执行蓝色箭头1,接着往下执行到蓝色箭头2第一次调用生成器取值,此时生成器函数lifang_generate()开始执行,执行到生成器函数lifang_generate()的蓝色箭头2碰到yield关键字,这时候生成器函数暂停往下执行并且将result的结果返回,由于是第一次执行,因此result存储着1的立方的值,此时将1返回,第54行代码print(first)将结果打印输出。
主函数中程序接着往下执行到蓝色箭头3,生成器函数lifang_generate()第二次被调用,与第一次不同,第二次从上一次(也就是第一次)暂停的位置继续往下执行,上一次停在了yield处,因此蓝色箭头3所作的事情就是执行yield后面的语句,也就是第48行print('end'),执行完成之后因for循环条件满足,程序像第一次执行那样,执行到yield处暂停并返回一个值,此时返回的是2的立方数,在第57行打印输出8。
第三次调用(蓝色箭头4)与第二次类似,在理清了执行过程之后,程序执行结果如下:

迭代器
这里先抛出两个概念:可迭代对象、迭代器。
凡是可以通过for循环遍历其中的元素的对象,都是可迭代对象;之前学习得组合数据类型list(列表)、tuple(元组)、dict(字典)、集合(set)等,上一小节介绍得生成器也可以使用for循环来遍历,因此,生成器也是迭代器,但迭代器不一定就是生成器,例如组合数据类型。
凡是可以通过next访问取值得对象均为迭代器,生成器就是一种迭代器。可以看到,生成器不仅可以用for循环来获取值,还可以通过next()来获取。
Python中有一个库collections,通过该库的Iterable方法来判断一个对象是否是可迭代对象;如果返回值为True则说明该对象为可迭代的,返回值为False则说明该对象为不可迭代。用Iterator方法来判断一个对象是否是迭代器,根据返回值来判断是否为迭代器。
使用Iterable分别判断列表,字典,字符串以及一个整数类型是否是可迭代对象的代码如下:
执行的输出结果如下:
使用Iterator判断一个对象是否是迭代器的代码如下,与判断是否为可迭代对象类似:
输出的结果如下:
组合数据类型不是迭代器,但是属于可迭代对象,可以通过iter()函数将其转换位迭代器,这样就可以使用next方法来获取对象各个元素的值,代码如下:
输出结果为:
闭包
内部函数对外部函数变量的引用,则将该函数与用到的变量称为闭包。以下为闭包的例子:
理解闭包是理解装饰器的前提,同样通过一张图来理解闭包的执行过程:

图解:Python解释器从上往下解释代码,首先定义一个函数,func指向了该函数(红箭头所示);接着到主函数执行第14行代码 ret = func(10),此时先执行赋值号“=”右边的内容,这里调用了函数func()并传入10这个实参,函数func()代码开始执行,先是打印输出“start”,接着定义了一个函数func_in(),func_in指向了该函数,函数没有被调用,程序接着往下执行,return func_in 将函数的引用返回,第14行代码用ret接收了这个返回值,到此ret就指向了func_in所指向的函数体(绿箭头所示)。最后执行ret所指的函数。这就是闭包的整个过程,func_in()函数以及该函数内用到的变量num就称为闭包。
装饰器
代码的编写需要遵循封闭开放原则,封闭是指对于已有的功能代码实现不允许随意进行修改,开放是指能够对已有的功能进行扩展。例如一款手游,现在已经能够实现现有的游戏模式,但随着外部环境的变化发展(市场竞争,用户体验等),现有的游戏模式已经不能满足用户的需求了。为了留住用户,需要加入更多的玩法来保持用户对该款游戏的新鲜感,于是开发方在原来游戏的基础上又开发了好几种游戏模式。像这样,新的游戏版本既增加了先的游戏模式,又保留了原有的游戏模式,体现了封闭开放的原则。 装饰器的作用就是在不改变原来代码的基础上,在原来的功能上进行拓展,保证开发的效率以及代码的稳定性。 打印输出九九乘法表可以通过以下代码实现:
输出结果如下: 假如现在需要实现一个功能,在不修改func_1函数代码的前提下,在九九乘法表前增加一个表头说明,在乘法表最后也增加一个说明。下面的代码实现了装饰器的功能:
输出结果如下: 可以看到func_1函数的代码没有任何修改,还实现了问题提出的要求,这其中的核心就在于最后两行代码。通过下图来理解装饰器执行的过程:

图解:跟之前一样,Python解释器自上往下解释代码,遇到定义函数的代码不用管,因为没有调用函数是不会执行的;这样直接就来到了第22行代码中,程序先执行赋值号“=”右边的代码,shuoming(func_1)调用了之前定义的函数,并传入了func_1实参,程序转到shuoming(func)执行,形参func接收实参func_1,此时func也指向了func_1所指向的函数(如图中分界线上方白色方框内的蓝箭头所示);在shuoming()函数中代码继续往下走,在shuoming()函数内容又定义了一个shuoming_in()函数(如图中分界线上方白色方框内的蓝色方框所示),接着往下,将shuoming_in()函数的引用返回,至此shuoming()函数执行完毕,程序回到第22行代码执行,shuoming()函数的返回值被func_1接收,此时,func_1不在指向原来的函数,转成指向shuoming_in所指向的函数(如图中分界线下方白色方框内的黄色箭头)。最后调用func_1所指向的函数,也就是shuoming_in()函数,shuoming_in()函数内的func指向了原来func_1()所指的函数(也就是生成九九乘法表的函数),因此程序最终的结果就在九九乘法表前后各加了一个说明性字符串。
以上为装饰器的执行过程,但是以上装饰写法不够简洁,大多数情况下采取以下写法,输出结果是一样的:
有时候有些被装饰的函数可能有以下几种情况:存在或不存在参数,有返回值或没有返回值,参数可能定长或不定长等等,为了通用性,与爬虫的请求代码一样,装饰器有着通用的写法:
使用这个装饰器装饰九九乘法表一样可以正常输出,如果需要特定的装饰效果,修改这个通用代码即可。
结束
以上为生成器、迭代器、闭包以及装饰器的所有内容,其中装饰器属于难点。理解装饰器的执行过程能够更好的帮助我们进阶学习Python。
Python核心编程的四大神兽:迭代器、生成器、闭包以及装饰器

2018-10-10
生成器
生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yield关键字时该函数会暂停执行该函数后续的代码,并且返回一个值;在下一次调用该函数执行时,程序将从上一次暂停的位置继续往下执行。
通过一个例子来理解生成器的执行过程。求1-10的所有整数的立方并将结果打印输出,正常使用列表的实现如下:
输出结果如下:
当数据量很少时,可以很快得到结果。但是如果范围扩大到10000甚至是100000000,就会发现程序执行时间会变长,变卡,甚至有可能会因超出内存空间而出现程序崩溃的现象。这是因为当数据量变得非常大的时候,内存需要开辟很大的空间去存储这些数据,内存都被吃了,自然会变慢变卡。使用生成器就能解决这个问题。
对于上述同一个问题用生成器实现如下,将范围扩大到1-10000000:
执行效果如下:

可以看到没有任何的结果输出,这说明程序已经可以顺利执行。对于迭代器来讲需要用next()方法来获取值,修改主函数为以下情况可以打印输出前4个整数的立方数:
输出结果如下:

到此可以看到,生成器生成的值需要使用next()方法一个一个的取,它不会一次性生成所有的计算结果,只有在取值时才调用,这时程序会返回计算的一个值且程序暂停;下一次取值时从上一次中断了的地方继续往下执行。
以取出前3个值为例,下图为生成器代码解析图:

图解:Python解释器从上往下解释代码,首先是函数定义,这时在计算机内存开辟了一片空间来存储这个函数,函数没有被执行,继续往下解释;到了主函数部分,首先执行蓝色箭头1,接着往下执行到蓝色箭头2第一次调用生成器取值,此时生成器函数lifang_generate()开始执行,执行到生成器函数lifang_generate()的蓝色箭头2碰到yield关键字,这时候生成器函数暂停往下执行并且将result的结果返回,由于是第一次执行,因此result存储着1的立方的值,此时将1返回,第54行代码print(first)将结果打印输出。
主函数中程序接着往下执行到蓝色箭头3,生成器函数lifang_generate()第二次被调用,与第一次不同,第二次从上一次(也就是第一次)暂停的位置继续往下执行,上一次停在了yield处,因此蓝色箭头3所作的事情就是执行yield后面的语句,也就是第48行print('end'),执行完成之后因for循环条件满足,程序像第一次执行那样,执行到yield处暂停并返回一个值,此时返回的是2的立方数,在第57行打印输出8。
第三次调用(蓝色箭头4)与第二次类似,在理清了执行过程之后,程序执行结果如下:

迭代器
这里先抛出两个概念:可迭代对象、迭代器。
凡是可以通过for循环遍历其中的元素的对象,都是可迭代对象;之前学习得组合数据类型list(列表)、tuple(元组)、dict(字典)、集合(set)等,上一小节介绍得生成器也可以使用for循环来遍历,因此,生成器也是迭代器,但迭代器不一定就是生成器,例如组合数据类型。
凡是可以通过next访问取值得对象均为迭代器,生成器就是一种迭代器。可以看到,生成器不仅可以用for循环来获取值,还可以通过next()来获取。
Python中有一个库collections,通过该库的Iterable方法来判断一个对象是否是可迭代对象;如果返回值为True则说明该对象为可迭代的,返回值为False则说明该对象为不可迭代。用Iterator方法来判断一个对象是否是迭代器,根据返回值来判断是否为迭代器。
使用Iterable分别判断列表,字典,字符串以及一个整数类型是否是可迭代对象的代码如下:
执行的输出结果如下:
使用Iterator判断一个对象是否是迭代器的代码如下,与判断是否为可迭代对象类似:
输出的结果如下:
组合数据类型不是迭代器,但是属于可迭代对象,可以通过iter()函数将其转换位迭代器,这样就可以使用next方法来获取对象各个元素的值,代码如下:
输出结果为:
闭包
内部函数对外部函数变量的引用,则将该函数与用到的变量称为闭包。以下为闭包的例子:
理解闭包是理解装饰器的前提,同样通过一张图来理解闭包的执行过程:

图解:Python解释器从上往下解释代码,首先定义一个函数,func指向了该函数(红箭头所示);接着到主函数执行第14行代码 ret = func(10),此时先执行赋值号“=”右边的内容,这里调用了函数func()并传入10这个实参,函数func()代码开始执行,先是打印输出“start”,接着定义了一个函数func_in(),func_in指向了该函数,函数没有被调用,程序接着往下执行,return func_in 将函数的引用返回,第14行代码用ret接收了这个返回值,到此ret就指向了func_in所指向的函数体(绿箭头所示)。最后执行ret所指的函数。这就是闭包的整个过程,func_in()函数以及该函数内用到的变量num就称为闭包。
装饰器
代码的编写需要遵循封闭开放原则,封闭是指对于已有的功能代码实现不允许随意进行修改,开放是指能够对已有的功能进行扩展。例如一款手游,现在已经能够实现现有的游戏模式,但随着外部环境的变化发展(市场竞争,用户体验等),现有的游戏模式已经不能满足用户的需求了。为了留住用户,需要加入更多的玩法来保持用户对该款游戏的新鲜感,于是开发方在原来游戏的基础上又开发了好几种游戏模式。像这样,新的游戏版本既增加了先的游戏模式,又保留了原有的游戏模式,体现了封闭开放的原则。 装饰器的作用就是在不改变原来代码的基础上,在原来的功能上进行拓展,保证开发的效率以及代码的稳定性。 打印输出九九乘法表可以通过以下代码实现:
输出结果如下: 假如现在需要实现一个功能,在不修改func_1函数代码的前提下,在九九乘法表前增加一个表头说明,在乘法表最后也增加一个说明。下面的代码实现了装饰器的功能:
输出结果如下: 可以看到func_1函数的代码没有任何修改,还实现了问题提出的要求,这其中的核心就在于最后两行代码。通过下图来理解装饰器执行的过程:

图解:跟之前一样,Python解释器自上往下解释代码,遇到定义函数的代码不用管,因为没有调用函数是不会执行的;这样直接就来到了第22行代码中,程序先执行赋值号“=”右边的代码,shuoming(func_1)调用了之前定义的函数,并传入了func_1实参,程序转到shuoming(func)执行,形参func接收实参func_1,此时func也指向了func_1所指向的函数(如图中分界线上方白色方框内的蓝箭头所示);在shuoming()函数中代码继续往下走,在shuoming()函数内容又定义了一个shuoming_in()函数(如图中分界线上方白色方框内的蓝色方框所示),接着往下,将shuoming_in()函数的引用返回,至此shuoming()函数执行完毕,程序回到第22行代码执行,shuoming()函数的返回值被func_1接收,此时,func_1不在指向原来的函数,转成指向shuoming_in所指向的函数(如图中分界线下方白色方框内的黄色箭头)。最后调用func_1所指向的函数,也就是shuoming_in()函数,shuoming_in()函数内的func指向了原来func_1()所指的函数(也就是生成九九乘法表的函数),因此程序最终的结果就在九九乘法表前后各加了一个说明性字符串。
以上为装饰器的执行过程,但是以上装饰写法不够简洁,大多数情况下采取以下写法,输出结果是一样的:
有时候有些被装饰的函数可能有以下几种情况:存在或不存在参数,有返回值或没有返回值,参数可能定长或不定长等等,为了通用性,与爬虫的请求代码一样,装饰器有着通用的写法:
使用这个装饰器装饰九九乘法表一样可以正常输出,如果需要特定的装饰效果,修改这个通用代码即可。
结束
以上为生成器、迭代器、闭包以及装饰器的所有内容,其中装饰器属于难点。理解装饰器的执行过程能够更好的帮助我们进阶学习Python。
Python核心编程的四大神兽:迭代器、生成器、闭包以及装饰器的更多相关文章
- Python核心编程的四大神兽
http://www.cnblogs.com/ssy3340/p/9747722.html
- python语法生成器、迭代器、闭包、装饰器总结
1.生成器 生成器的创建方法: (1)通过列表生成式创建 可以通过将列表生成式的[]改成() eg: # 列表生成式 L = [ x*2 for x in range(5)] # L = [0, 2, ...
- python中“生成器”、“迭代器”、“闭包”、“装饰器”的深入理解
python中"生成器"."迭代器"."闭包"."装饰器"的深入理解 一.生成器 1.生成器定义:在python中,一边 ...
- python 生成器,迭代器,闭包,装饰器
1.生成器,迭代器,闭包,装饰器的优点 生成器就是一类特殊的迭代器 迭代器的优点也即生成器的优点: 1.节约内存.python在使用生成器时对延迟操作提供了支持. 2.迭代到下一次的调用时,所使用的参 ...
- python中的生成器、迭代器、闭包、装饰器
迭代器 迭代是访问集合元素的一种方式.迭代器是一个可以记住遍历的位置的对象.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 可迭代对象 以直接作用于 for ...
- python中“生成器”、“迭代器”、“闭包”、“装饰器”的深入理解
一.生成器 1.什么是生成器? 在python中,一边循环一边计算的机制,称为生成器:generator. 2.生成器有什么优点? 1.节约内存.python在使用生成器时对延迟操作提供了支持.所谓延 ...
- python函数式编程之返回函数、匿名函数、装饰器、偏函数学习
python函数式编程之返回函数 高阶函数处理可以接受函数作为参数外,还可以把函数作为结果值返回. 函数作为返回值 def laxy_sum(*args): def sum(): ax = 0; fo ...
- python中对变量的作用域LEGB、闭包、装饰器基本理解
一.作用域 在Python程序中创建.改变.查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域.python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量 ...
- python tips:最内嵌套作用域规则,闭包与装饰器
在作用域与名字空间提到,python是静态作用域,变量定义的位置决定了变量作用的范围.变量沿着local,global,builtins的路径搜索,直觉上就是从里到外搜索变量,这称为最内嵌套作用域规则 ...
随机推荐
- [SHOI2015]聚变反应炉[树dp、贪心]
题意 给定一棵 \(n\) 个点的树,每个点有一个启动能量 \(d\) 和传递能量 \(c\) ,如果一个点被启动了,就会向和他直接相连的点发送 \(c\) 的能量,初始所有节点能量为0,问最少多少能 ...
- REST-framework快速构建API--四部曲
代码目录结构: 一.使用原生APIView 使用rest-framework原生的APIView实现过程: 以url(r'^books/$', views.BookView.as_view(),nam ...
- 【Direct2D1.1初探】Direct2D特效概览
转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 Direct2D是一个基于Direct3D的2D图形API,可以利用硬件加速特性来提供高性能高质量的2D渲染.但 ...
- node基础-文件系统-文件写操作
文件操作频率最高的就是读跟写.nodejs的文件的读取API在<node基础-文件系统-读取文件>里已经简单介绍过,本文就简单介绍下nodejs的文件写API. nodejs的文件操作均提 ...
- LintCode——Pour Water
Pour Water: We are given an elevation map, heights[i] representing the height of the terrain at that ...
- Muduo学习笔记(一) 什么都不做的EventLoop
Muduo学习笔记(一) 什么都不做的EventLoop EventLoop EventLoop的基本接口包括构造.析构.loop(). One Loop Per Thread 一个线程只有一个Eve ...
- DevOps架构下如何进行微服务性能测试?
一. 微服务架构下的性能测试挑战 微服务与DevOps 微服务是实现DevOps的重要架构 微服务3S原则 DevOps核心点 微服务架构下的业务特点 亿级用户的平台 单服务业务随时扩容 服务之间存在 ...
- MongoDB作为Windows服务来安装 错误1053:服务没有及时响应启动或控制请求
这个问题我解决了一晚上,用尽了所有百度 博客上的方法,都是失败的 结果重新换了一种安装的方法 视频讲解 非常清楚 https://www.bilibili.com/video/av31240330? ...
- Linux读书笔记第三、四章
第三章 主要内容: 进程和线程 进程的生命周期 进程的创建 进程的终止 1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作(比如,创建,销毁等)都是有内核来实现的. Li ...
- 《Linux 内核分析》第五周
[李行之原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...