装饰器-推导式-生成器-迭代器
一、装饰器
(一)装饰器的引入
- 我们想为函数添加更多功能,如果直接在原来的基础上修改,会出现以下问题:
- 如果修改的函数多,修改起来会比较麻烦
- 不方便后期的维护
- 这样做会违反开闭原则(ocp–open close prinsaple)
ocp原则:程序的设计,要求开发对程序的扩展,要关闭对程序的修改,开放对代码的扩展,关闭代码的修改
- 如果之前引用过,那么你直接改掉会影响很大,所以我们最好是对函数扩展
(二)装饰器的作用
- 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
- 在开发中,我们都是通过装饰器来扩展函数的功能的
(三)装饰器的创建
- 首先是闭包函数
- 利用@调用
原函数需要装饰器,但是我们调用的是原函数,加一个@原函数就加上了该设备,叫装饰器的语法糖@fun,相当于fun1(fun) ,就可以在原函数调用了
- 过程解析:首先要调用函数,然后内层函数暂时不会执行,然后外层函数的调用=外层函数的返回值,然后外层函数的返回值是内层函数的函数对象,内层函数的调用等于内层函数的函数对象加一个括号,也就是调用,那么也就是外层函数的调用加上括号就是对内层函数的调用
二、推导式
(一)推导式的引入
- 代码的可读性:使用for循环来实现相同的功能,需要好几行代码,而列表推导式只需要一行代码.
- 在Python3中列表推导式有自己的局部作用域,就像函数似的.表达式内部的变量和赋值只在局部起作用,表达式的上下文里的同名变量还可以被正常引用,局部变量并不会影响到它们.
(二)推导式的定义
- 推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。
- 代码超过了两行,就要考虑改成用for循环了
(三)推导式创建
- python中有三种的推导式,它们分别为:列表推导式、字典推导式、集合(set)推导式
列表推导式
- 变量名= [表达式 for 变量 in 可迭代对象 if 条件]
- 可以嵌套for循环
- 可以嵌套for循环
字典推导式
- 变量名={v:k for v,k in 字典.items() if 条件语句}
变量名={v:k for v,k in 双值子序列 if 条件语句}
集合(set)推导式
- 变量名={表达式 for 变量 in 可迭代对象}
变量名= {表达式 for 变量 in 可迭代对象 if 条件}- 可迭代对象:range()、list、dict(只会存key)、string、tuple
- 集合是无序且不重复的,所以会自动去掉重复的元素,并且每次运行显示的顺序不一样
三、迭代器
(一)迭代器简介
定义
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterator
- 迭代器指的是迭代取值的工具,迭代是指一个重复的过程,每一次重复都是基于上一次的结果而来的
特点
- 迭代提供了一种通用的不依赖索引的迭代取值方式
- 迭代是Python最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 永远返回的是自身
迭代器与可迭代对象区别 - 迭代器: 可以用next()作用的都是迭代器类型
- 迭代器一定是可迭代对象
- 生成器也是迭代器
- 可迭代对象: 可以用for循环遍历的对象都是可迭代对象
- str、list、tuple、dict、set、等都是可迭代对象,可以通过iter()函数将他们转为迭代器
- python的for循环本质就是通过不断调用next()函数实现的
(二)迭代器的创建
方法有两种:iter函数、类创建
iter()函数
- 将可迭代的对象变成迭代器
__iter__ 与 __next__类创建
- 两个方法都有的话就是迭代器
- _iter_()方法:
- 返回一个特殊的迭代器对象,一定要return self
- 有__iter__(),这里实例对象就是可迭代对象,再加__next__就是迭代器
- _next_() 方法:
- 返回下一个对象。并通过 StopIteration 异常标识迭代的完成(就是调用次数超过元素个数时报的异常)。
- 如果没有__iter__(),next也可以返回下一个数字,但是这样产生的就不是迭代器,就用不了for循环
- _iter_()方法:
(三)迭代器的使用
- 直接调用不能将数显示出来,要借用next()
- 迭代器对象可以结合常规for或while语句进行遍历
next()
- next(iterator, default=None)
- iterator:传入的必须是迭代器
- 将迭代器调用出来
四、生成器
(一) 生成器简介
引入
- 通过列表推导式我们可以直接创建出一个列表,但是受到内存的限制,我们不可能创造出一个无限大的列表。而且创建一个有200万个元素的列表,会占用很大的内存空间,而这个时候我们仅仅需要访问列表中几个元素,那么后面的元素就占用着空间就是一种浪费的行为。
- 那么我们可不可以用几个元素就创建出几个元素。这样在一定程度上就优化了内存。所以引入了生成器
生成器
- 一边循环一边计算的机制就是生成器
- 也是一种迭代器
特点
- 会删掉已经执行的数据。
- 你取出去了这个数据,那么这个数据在生成器里面就不存在了,当你把生成器里的数据取完了,那么它就不能够继续使用了,也就是说生成器只能用一次
(二) 生成器的创建
两种方法:类似列表推导式、利用yield
类似列表推导式
- 方法和列表推导式一样,只是把外符号换成()
利用yield
yield定义
- 只要在函数中出现yield关键字它就是一个生成器函数
- 这个就是我们的这个yield关键字的效果,只要我们在函数中使用了yield关键字,那么它不在是一个函数,而是一个生成器
yield执行顺序:
- 第一调用的时候是到yield那行代码结束
- 我们第二次调用的时候,并不是自上往下执行的,而是从上一次停止的位置继续执行的,这也就是我们的生成器的特性,它会记住你上一次停留的地方,下次继续从这个地方开始执行。
- 到yield结束运行。那么这个原因还是和它变成了一个生成器的原因所导致的。生成器一样的会和input函数一样起一个类似的阻塞作用,需要你调用它内部的next方法才会使程序执行一次,当你再一次遇到yield的时候,同样又会停止
yield生成器的返回值
- 可以设置返回值
- 返回值是当值取完的时候会返回的值
(三) 生成器的使用与关闭
- 生成器的使用:next(迭代器) 、 生成器.send()
- 生成器的关闭:生成器.close()
生成器.send()
- res = 生成器.send(a)
- a :
- 第一次调用的生成器传进去的值必须是None,也可以用next先启用第一次
- 可以传任意值,传的值是等于res
- res= yield n
print(res)- 就会打印上一次调用send括号里的值
- 就会打印上一次调用send括号里的值
- a :
生成器.close()
- close() 关闭后就不能调用next或send
验证生成器会删掉已经执行的数据
- response = None这块,为什么会出现这么个结果,是不是有同学会纳闷,它的结果不应该是1吗?怎么会是None呢?这里我要告诉大家的是,这个1的结果已经被我们的yield返回出去了,所以这里没有数据,这也是生成器的另一个特征之一
小练习
1.请使用装饰器实现已存在的函数的执行所花费的时间。
def fun1(fn):
import time
def fun2():
time1=time.time()
fn()
time2=time.time()
print('函数所耗时为:%f'%(time2-time1))
return fun2
@fun1
def fun():
s=0
for i in range(100000):
pass
fun()
总结:
装饰器
为了给原来的函数扩展,不该原来的情况下
装饰器:
是一个闭包
用@加在原来的函数上
闭包的要求:
函数嵌套
嵌套的函数要用到外部函数的值
外部函数返回的是嵌套函数对象
高阶函数:
将函数作为返回值返回
接受函数作为参数
推导式
分为列表推导式、字典推导式、集合推导式
列表推导式:[i for i in range(6) if 条件语句]
字典推导式:{v:k for i in dict1.items() if 条件语句} 与 {v:k for i in 双值子序列 if 条件语句}
集合推导式:{i for i in range(6)if 条件语句} 会自动删去重复的
迭代器
可以被next引用的容器
创建:iter() 与__iter__和__next__结合
iter()只要是可迭代的数据类型都可以转化为迭代器,基本数据类型都可以应用
__iter__和__next__更像是创建了一个可以实现迭代的类,类实例化的每一个对象都是迭代器
__iter__将对象变成可迭代的对象,实现__next__就是迭代器
__iter__返回下一个的,可以在这里添加判断,抛出异常
迭代器的调用
for
for i in 迭代器:
print(i)
next(迭代器)
生成器.send(数字)
第一次调用的时候要么传None,要么先用next调用一下
生成器
引入
用几个就创建几个
创建
类似的列表推导式(i for i in range() if 条件语句)就是把列表推导式的[]换成()
yield 有yield的就是生成器
def fun():
num
while True:
num+=1
yield num
a=fun()
next(a)