Python迭代器&生成器&装饰器
1. 迭代器
1.1 可迭代对象(Iterator)
迭代器协议:某对象必须提供一个__next__()方法,执行方法要么返回迭代中的下一项,要么引起一个Stopiteration异常,以终止迭代(只能往后走,不能往前退)
协议是一种规范,可迭代对象实现了迭代器协议,python的内部工具(如for循环、sum、min、max函数),使用迭代器协议访问对象
可迭代对象(Iterator):实现了迭代器协议的对象(如何实现:对象内部定义了一个__iter__()方法),也就是可迭代对象内部要包含__iter__() 函数
迭代器(Iterator):内部包含了__iter()__() ,同时也包含__next__()
1.2 for循环的工作机制
先通过__iter__()方法将可迭代对象转换为迭代器,再调用迭代器中的__next__()方法遍历,最后再抓取结束时的异常
print("解析for循环的工作机制:")
num = [1, 2, 3, 4, 5]
for i in num: # 工作机制: num.__iter__() ------> num.__next__()
print(i)
1.3 可迭代对象与迭代器
1)可迭代对象与迭代器之间的关系
- 可迭代对象包含迭代器
- 如果一个对象拥有__iter__方法,其就是可迭代对象(可以被for循环迭代);如果一个对象拥有next方法,其是迭代器
- 定义可迭代对象,必须事先__iter__方法;定义迭代器,必须实现__iter__和next方法。
2)迭代器的特点
- 节省内存
- 惰性机制
- 不能反复,只能向下执行
3)isinstance()判断对象是否为Iterable对象:
from collections import Iterable
print(isinstance([], Iterable))
1.4 迭代器协议的使用示例
print("使用while循环遍历一个列表:")
index = 0
while index < len(num):
print(num[index])
index += 1 print("利用迭代器协议遍历一个列表:")
iter = num.__iter__()
print(iter.__next__())
print(iter.__next__())
print(iter.__next__()) print("解析文件操作中对于文件内容的遍历:")
f = open("test.txt", "r")
f_iter = f.__iter__() # 这里先将整个文件转换为一个迭代器,之后对迭代器调用__next__()方法,只在有需要的时候才加载文件一行内容
# 当取出一行内容时,因为没有赋值给任何变量,所以占用的内存会被python的自动回收机制回收,所以这种遍历文件的方式只会动态的占用一小块内存
print(f_iter.__next__(), end="")
print(f_iter.__next__(), end="")
print(f_iter.__next__(), end="")
print(f_iter.__next__(), end="")
print(f_iter.__next__(), end="")
f.close() print("next()方法:") # next()方法--->调用__next__()方法
the_num = [1, 2, 3]
the_iter = the_num.__iter__() # 也可以直接使用iter(the_num)方法
print(next(the_iter)) # next(the_iter) -----> 调用 the_iter.__next__()
2. 生成器
2.1 生成器概述(generator)
生成器(generator)就是可迭代对象(它在内部实现了迭代器协议)
生成器在python中的两种表达形式:
- 生成器表达式
- 生成器函数
触发生成器的方式:
- 通过调用__next__()方法,相当于send(None)
- 通过调用send()方法
2.2 生成器函数
- 只要函数中包含yield关键字,则此函数就是一个生成器函数
- 每调用一次 __next__(),yield后面的值就会被返回一次,且保留此次的状态,下一次调用从此位置开始
1)生成器函数
def func():
print("现在开始执行生成器函数:")
print("First----->")
yield "第一步"
print("Second----->")
yield "第二步"
print("Third")
yield "第三步"
print("End")
yield "生成器函数调用完毕" f = func()
print(f.__next__()) # __next__()函数调用接受到的返回值就是yield后面的值
print(f.__next__())
print(f.__next__())
print(f.__next__())
2)用yield方式的生成器实现字典中文件的内容查找
def get_num():
f = open("population.txt", "r", encoding="utf-8") # 这个文件中的每一行都是一个字典
for i in f:
yield i # 这里返回的 i 为字符串类型 population = eval(get_num().__next__()) # 这里通过eval()函数重新恢复了文件中数据的数据类型,恢复成了字典
print("%s的人口数为%s" % (population["city"], population["population"]))
# 注意:生成器只能遍历一次 # 用生成器表达式的方式实现字典中文件数字的求和
f = open("population.txt", "r", encoding="utf-8")
p = sum(eval(i)["population"] for i in f)
print(p)
3)send()方法的使用
def my_func():
print("First")
send1 = yield 1 # yield后面的值是next方法调用的返回值,而前面的值是yield接收值之后赋值的变量
print("Second")
print("此次send传送的值为:", send1)
send2 = yield 2
print("Third")
print("此次send传送的值为:", send2)
yield 3
print("End") m = my_func() # 这里并不会执行这个函数,因为只要函数内部定义了yield关键字,函数就变成了生成器,只有通过next方法调用
print(m.__next__()) print(m.send(None) ) # 这里的send(None),就相当于__next__()方法,触发一次生成器
# send()函数将值传递给当前停留位置的yield,之后这个yield会将值传递给前面的变量
print(m.send("这是第二次send"))
4)yield from
- yield from可以直接把迭代对象中的每一个数据作为生成器的结果进行返回
- 因为yield from是将列表中的每一个元素返回,所以写两个yield from并不会产生交替的效果
def func():
lst1 = ['卫龙','老冰棍','北冰洋','牛羊配']
lst2 = ['馒头','花卷','豆包','大饼']
yield from lst1
yield from lst2
g = func()
for i in g:
print(i) # 卫龙
# 老冰棍
# 北冰洋
# 牛羊配
# 馒头
# 花卷
# 豆包
# 大饼
5)额外
def func3():
for i in range(10):
yield i t = func3()
# for i in t: # 因为for循环的工作机制就是使用迭代器协议调用next方法,所以会触发生成器函数
# print(i) li1 = (i for i in t) # 这里li1会得到一个生成器的地址
li2 = (i for i in li1)
print(type(li1))
print(list(li1)) # 这里的list函数中会用for循环机制取值,从而触发生成器,现在这个位置li1中的值已经被取完了
print(list(li2)) # 因li1中的值已经被取完了,所以li2现在已经取不到值了
2.3 推导式
- 推导式有:列表推导式、字典推导式、集合推导式
- 没有元组推导式
1)列表推导式
- 三元表达式
name = "hgzero"
ret = "me" if name == "hgzero" else "other" # 若name等于hgzero,则返回me,否则返回other
print(ret)
- 列表推导式(列表解析)
list1 = [i for i in range(10)] # 列表解析会将生成的列表直接放于内存中,会占用较大的内存
print(list1)
list2 = ["数字%d" % i for i in range(10)] # for循环处理后将i依次往前面传递
print(list2)
- 筛选模式(三元表达式和列表解析的使用示例)
print("三元表达式和列表解析结合")
list3 = ["数字%d" % i for i in range(10) if i > 5] # 这里不能使用else,因为已经构成了三元表达式了
print(list3)
2)生成器推导式
- 将列表推导式外围的中括号换成小括号就变成了一个生成器推导式
- 生成器推导式也可以使用筛选模式
print("生成器表达式:")
list2 = ("数字%d" % i for i in range(10)) # 将列表解析表达式外围的中括号换成小括号就变成了一个生成器
# 生成器表达式更节省内存
print(list2)
print(list2.__next__())
print(list2.__next__())
print(list2.__next__())
print(list2.__next__()) # 可以将生成器表达式传递给sum()等函数,以节省占用内存的大小
print(sum(i for i in range(100000)))
# 这里的sum函数里面的生成器表达式不需要加括号
# 如果直接在sum中传入一个非常大的列表,会造成占用太多内存而导致机器卡死,而用生成器的方式传入则会动态的占用很小的一片内存
3)字典推导式
lst1 = ['jay','jj','huazai']
lst2 = ['周杰伦','林俊杰','刘德华']
dic = {lst1[i]:lst2[i] for i in range(len(lst1))}
print(dic)
4)集合推导式
- 集合推导式可以直接生成一个集合
- 集合的特点:无需、不重复,所以集合推导式自带去重功能
lst = [1,2,3,-1,-3,-7,9]
s = {abs(i) for i in lst}
print(s)
2.4 生产者消费者模型
import time # def producer():
# ret = []
# for i in range(100):
# time.sleep(0.1)
# ret.append("包子%s" % i)
# return ret
#
# def consumer(ret):
# for index, baozi in enumerate(ret):
# time.sleep(0.1)
# print("第%d个人来了,吃了第%s个包子" % (index, baozi))
#
# p = producer()
# consumer(p) def producer():
c1 = consumer("hgzero")
c2 = consumer("wuzhihao")
c1.__next__()
c2.__next__()
for i in range(10):
time.sleep(1)
print("现在包子 %s 已经做好了!" % i)
c1.send("第%s个包子" % i)
c2.send("第%s个包子" % i) def consumer(name):
print("吃包子开始!")
while True:
baozi = yield
time.sleep(1)
print("我是 %s , 现在开始吃 %s 包子" % (name, baozi)) producer()
2.5 闭包
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包。简单来说,闭包就是内层函数,对外层函数变量的引用。
一般情况下,如果一个函数执行结束,则这个函数的内部的变量以及局部命名空间中的内容都会被释放掉。但是在闭包中,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。也就说,使用闭包,可以保证外部函数中的变量在内存中常驻,以供后面的程序使用。
其实闭包中外部函数的返回值定义成内部函数的地址,是为了防止内部函数被垃圾回收机制回收。
def func1():
def func2():
s = '地主家的傻儿子'
def func3():
print(s)
return func3
return func2
func1()()()
2.6 解压序列
# 解压序列:
a, *b, c = [1, 2, 3, 4, 5, 6, 7, 8, 9] # 这里的 * 号表示所有的意思
print(a)
print(b)
print(c) # 交换两个变量的值:
a = 1
b = 2
print("通过中间变量的形式交换:")
c = a
a = b
b = c
print(a, b) print("另外两种不引入额外变量实现交换的方式:")
f1 = 1
f2 = 2
f1, f2 = f2, f1
print(f1, f2) n1 = 1
n2 = 2
n1 = n1 + n2
n2 = n1 - n2
n1 = n1 - n2
print(n1, n2)
3. 装饰器
3.1 装饰器概述
- 装饰器:本质上就是函数,功能是为其他函数添加附加功能
- 原则:
- 不修改被修饰函数的源代码
- 不修改被修饰函数的调用方法
- 装饰器 = 高阶函数 + 函数嵌套 + 闭包
3.2 装饰器的实现方式
1)用高阶函数的方式实现装饰器的功能:不合格,因为这种方式会使工作函数调用了两次
# 1.不修改被修饰函数的源代码
# 2. 不修改被修饰函数的调用方式 # 装饰器 = 高阶函数+函数嵌套+闭包
import time # 用高阶函数的形式想实现装饰器的功能,不合格,因为这种方式会使工作函数调用了两次
def work_func():
time.sleep(1)
print("这里是work_func函数") def dec_func(func):
start_time = time.time()
func()
finish_time = time.time()
print("这个函数运行的时间是%d" % (finish_time-start_time))
return func work_func = dec_func(work_func) # 这里在赋值之前就已经执行了dec_func(work_func)函数
work_func() # 这里又执行了一便work_func函数
2)用高阶函数 + 函数嵌套 + 闭包的方式实现装饰器
import time
def work_func2():
time.sleep(1)
print("这里是work_func2函数") def dec_func2(func):
def wapper():
start_time = time.time()
func()
finish_time = time.time()
print("这个函数运行的时间是%d" % (finish_time - start_time))
return wapper work_func2 = dec_func2(work_func2) # 这里执行dec_func2函数时遭遇一个闭包,得到的是闭包wrapper函数的内存地址(并没有执行它)
work_func2() # 这里执行上面得到的闭包函数,即执行wapper()函数
3)用“语法糖”的形式实现装饰器
import time
def dec_func3(func):
def wapper():
start_time = time.time()
func()
finish_time = time.time()
print("这个函数运行的时间是%d" % (finish_time - start_time))
return wapper @dec_func3 # 用“语法糖”的形式实现装饰器 ,相当于 work_func3 = dec_func3(work_func3)
def work_func3():
time.sleep(1)
print("这里是work_func3函数")
work_func3() def dec_func4(func): # 装饰器改进,添加返回值和多参数功能
def wapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs) # func()函数的返回值就是work_func函数中的返回值,用res接收之后,再返回出去
finish_time = time.time()
print("这个函数运行的时间是%d" % (finish_time - start_time))
return res
return wapper @dec_func4 # work_func4 = dec_func3(work_func3) 这时执行work_func4函数就是执行wapper函数,因为dec_func3函数返回的就是wapper的地址
def work_func4(name, age):
time.sleep(1)
print("这里是work_func4函数")
print("Name:【%s】,Age:【%d】" % (name, age))
return 12345 # 这时,work_func函数的返回值就是dec_func函数的闭包---> wapper函数的返回值
work_func4("hgzero", 21)
3.3 带认证功能的装饰器
import time user = [
{"username": "hgzero", "passwd": "123"},
{"username": "wzh", "passwd": "123"},
{"username": "hg", "passwd": "123"},
{"username": "zero", "passwd": "123"},
{"username": "abc", "passwd": "123"}
] login_dict = {"username": None, "login": False} # 这里用这个字典模拟session的功能 def login_func(func):
def wrapper(*args, **kwargs):
if login_dict["username"] and login_dict["login"]:
ret = func(*args, **kwargs)
return ret
while True:
username = input("请输入用户名:").strip()
passwd = input("请输入密码:").strip()
for dict in user:
if dict["username"] == username and dict["passwd"] == passwd:
login_dict["username"] = username
login_dict["login"] = True
ret = func(*args, **kwargs)
return ret
else:
print("用户名或密码错误!")
time.sleep(1)
print("请重新输入----->")
return wrapper @login_func
def index():
print("欢迎来到zero的网站主页!") @login_func
def home(name):
print("【%s】,欢迎来到个人中心!" % (name)) @login_func
def shopping(name):
print("【%s】,现在您的购物车中有 %s , %s , %s " % (name, "牛奶", "香蕉", "芒果")) index()
home("python工程师")
shopping("python工程师")
3.4 带参数的装饰器
import time
user = [
{"username": "hgzero", "passwd": "123"},
{"username": "wzh", "passwd": "123"},
{"username": "hg", "passwd": "123"},
{"username": "zero", "passwd": "123"},
{"username": "abc", "passwd": "123"}
] login_dict = {"username": None, "login": False} # 这里用这个字典模拟session的功能 def func(db_type):
def login_func(func):
print("这个数据库的类型是:", db_type)
def wrapper(*args, **kwargs):
if login_dict["username"] and login_dict["login"]:
ret = func(*args, **kwargs)
return ret
while True:
username = input("请输入用户名:").strip()
passwd = input("请输入密码:").strip()
for dict in user:
if dict["username"] == username and dict["passwd"] == passwd:
login_dict["username"] = username
login_dict["login"] = True
ret = func(*args, **kwargs)
return ret
else:
print("用户名或密码错误!")
time.sleep(1)
print("请重新输入----->")
return wrapper
return login_func @func("MySQL") # 这里先执行func("MySQL") ---> index = login_func(index)
# 这里因为后面加了括号,所以是执行了func函数,执行了func函数所得到的返回值就是login_func函数的地址
# func()函数执行得到的地址就是login_func,所以这里就等价于是 @login_func,同时也将参数传进去了,巧妙的运用了闭包的原理
def index():
print("欢迎来到zero的网站主页!") @func("Mongodb")
def home(name):
print("【%s】,欢迎来到个人中心!" % (name)) @func("Redis")
def shopping(name):
print("【%s】,现在您的购物车中有 %s , %s , %s " % (name, "牛奶", "香蕉", "芒果")) index()
home("python工程师")
shopping("python工程师")
Python迭代器&生成器&装饰器的更多相关文章
- Python 迭代器&生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发
本节大纲 迭代器&生成器 装饰器 基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - ...
- 4.python迭代器生成器装饰器
容器(container) 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中.通常这类数据结构把所有的元素存储在内存中 ...
- Python迭代器,生成器,装饰器
迭代器 通常来讲从一个对象中依次取出数据,这个过程叫做遍历,这个手段称为迭代(重复执行某一段代码块,并将每一次迭代得到的结果作为下一次迭代的初始值). 可迭代对象(iterable):是指该对象可以被 ...
- Python(迭代器 生成器 装饰器 递归 斐波那契数列)
1.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优 ...
- Python基础-迭代器&生成器&装饰器
本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1.列表生成式,迭代器&生成器 列表生成式 我现在有个需求,看 ...
- 迭代器/生成器/装饰器 /Json & pickle 数据序列化
本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1.列表生成式,迭代器&生成器 列表生成式 孩子,我现在有个需 ...
- Day04 - Python 迭代器、装饰器、软件开发规范
1. 列表生成式 实现对列表中每个数值都加一 第一种,使用for循环,取列表中的值,值加一后,添加到一空列表中,并将新列表赋值给原列表 >>> a = [0, 1, 2, 3, 4, ...
- python学习笔记(5)--迭代器,生成器,装饰器,常用模块,序列化
生成器 在Python中,一边循环一边计算的机制,称为生成器:generator. 如: >>> g = (x * x for xin range(10)) >>> ...
- python中的迭代器&&生成器&&装饰器
迭代器iterator 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束. 迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外, ...
随机推荐
- 第14章——高级IO函数
1.套接字超时 套接字IO函数设置超时的方法有三种: (1)调用alarm. (2)select (3)使用SO_RECTIMEO和 SO_SNDTIMEO 选项 上面三种方法适用于输入输出操作(re ...
- 结合实战和源码来聊聊Java中的SPI机制?
写在前面 SPI机制能够非常方便的为某个接口动态指定其实现类,在某种程度上,这也是某些框架具有高度可扩展性的基础.今天,我们就从源码级别深入探讨下Java中的SPI机制. 注:文章已收录到:https ...
- Win10系统下安装VC6.0教程
学习一门语言最重要的一步是搭建环境,许多人搭建在搭建环境上撞墙了,就有些放弃的心理了:俗话说,工欲善其事,必先利其器:所以接下来我们进行学习C的第一步下载编程所用的工具;当然也有其它的软件,只不过初学 ...
- Mac 安装并破解热部署插件JRebel详细教程
热部署插件 JRebel 非常高效的解决了在IDEA中对 Java 代码修改重启Tomcat的效率问题,这样大大降低了项目开发的时间,不用重启即可重新编译修改过的Java代码! (1)IDEA中安装热 ...
- Spring Cloud Security OAuth2.0 认证授权系列(一) 基础概念
世界上最快的捷径,就是脚踏实地,本文已收录[架构技术专栏]关注这个喜欢分享的地方. 前序 最近想搞下基于Spring Cloud的认证授权平台,总体想法是可以对服务间授权,想做一个基于Agent 的无 ...
- 设置cmd默认管理员模式启动
打开cmd文件位置
- Ⅶ. Policy Gradient Methods
Dictum: Life is just a series of trying to make up your mind. -- T. Fuller 不同于近似价值函数并以此计算确定性的策略的基于价 ...
- appium每次运行不初始化
最近测试公司软件遇到一个问题,每次运行Appium测试代码,打开软件都是初始化,每次的新手教程以及无法记住密码一堆问题,导致效率很低,而且定位有时候出问题. 先看初始化的代码 1 from appiu ...
- LIS问题$n log_2 n$做法(二分优化)
#include<bits/stdc++.h> using namespace std; const int inf=1e9+5; const int maxn=1e6+5; int n, ...
- Forethought Future Cup - Final Round (Onsite Finalists Only) C. Thanos Nim 题解(博弈+思维)
题目链接 题目大意 给你n堆石子(n为偶数),两个人玩游戏,每次选取n/2堆不为0的石子,然后从这n/2堆石子中丢掉一些石子(每一堆丢弃的石子数量可以不一样,但不能为0),若这次操作中没有n/2堆不为 ...