Python全栈开发之4、迭代器、生成器、装饰器
一、迭代器
1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器
2、什么是可迭代对象?
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__ #字符串
(1,2,3).__iter__ #元组
[1,2,3].__iter__ #列表
{'a':1}.__iter__ #字典
{'a','b'}.__iter__ #集合
open('a.txt').__iter__ #文件
3、什么是迭代器?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
但是,list
、dict
、str
等可迭代对象并没有__next__()方法,所以它不是迭代器
可以把使用iter()
函数把list
、dict
、str
等可迭代对象变成迭代器
from collections import Iterator a=isinstance([],Iterator)
b=isinstance((),Iterator)
c=isinstance({},Iterator)
d=isinstance('abc',Iterator)
e=isinstance((i for i in range(10)),Iterator)
f=isinstance(100,Iterator)
print(a)
print(b)
print(c)
print(d)
print(e)
print(f) 输出:
False
False
False
False
True
False
使用isinstance()判断一个对象是否是可迭代对象
str1='hello' #字符串
tuple1=(1,2,3) #元组
list1=[1,2,3] #列表
dict1={'a':1} #字典
set1={'a','b'} #集合 #使用iter()方法把可迭代对象变成迭代器
iter_str=iter(str1)
iter_tuple=iter(tuple1)
iter_list=iter(list1)
iter_dict=iter(dict1)
iter_set=iter(set1) print(iter_str.__next__())
print(iter_tuple.__next__())
print(iter_list.__next__())
print(iter_dict.__next__())
print(iter_set.__next__())
把list、dict、str等可迭代对象变成迭代器
4、for循环
#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
print(dic[k]) #for循环的工作原理 #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环
5、迭代器的缺点
#优点:
- 提供一种统一的、不依赖于索引的迭代方式
- 惰性计算,节省内存
#缺点:
- 无法获取长度(只有在next完毕才知道到底有几个值)
- 一次性的,只能往后走,不能往前退
二、生成器
1、什么是生成器?
可以理解为一种数据类型、这种数据类型实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__()方法才可以),所以生成器就是可迭代对象。
2、生成器在Python中的表现形式
1、生成器函数
常规函数定义、但是是使用yield语句而不是使用return返回结果
yield语句一次返回一个结果、在每个结果中间挂起函数的状态 2、生成器表达式
类似于列表推导、但是生成器返回按需产生一个对象而不是一次构建一个结果列表
3、列表生成式
现在有一个需求,需要把列表[0,1,2,3,4,5,6,7,8,9]里的每个元素都加1,怎么实现?有以下几种方式:
list1=[0,1,2,3,4,5,6,7,8,9] for index,i in enumerate(list1):
list1[index]=i+1 print(list1) #原值修改 或者 a=[]
for i in range(10):
a.append(i*2)
print(a)
列表生成式写法
num_list = [ i*2 for i in range(10) ] #或者赋值
num_list = [ "数字%s"%i for i in range(10) ]
print(num_list) #输出:
['数字0', '数字1', '数字2', '数字3', '数字4', '数字5', '数字6', '数字7', '数字8', '数字9'] #结合三元表达式 num_list = [ "数字%s"%i for i in range(10) if i > 5] print(num_list)
#输出:
['数字6', '数字7', '数字8', '数字9']
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个生成器:
num_list = ("数字%s"%i for i in range(10) if i > 5) print(num_list.__next__())
print(num_list.__next__())
print(num_list.__next__()) #输出:
数字6
数字7
数字8
4、yield生成器函数
只要函数中存在yield,这个函数就是一个生成器函数
yield功能:1、相当于return一个返回值。2、保存函数的状态,以便下次从它离开的地方执行
def test(): yield 1
yield 2
yield 3
yield 4 num=test()
print(num.__next__())
print(num.__next__())
print(num.__next__())
print(num.__next__())
#输出:1
#
#
#
def fib(max):
n,a,b=0,0,1
while n<max:
# print(b)
yield b
a,b=b,a+b
n=n+1 gen=fib(10)
for i in gen:
print(i)
yield实现斐波拉契数列
import time
def consumer(name):
print("%s准备吃包子了"%name)
while True:
baozi=yield print('包子[%s]来了,被%s吃了'%(baozi,name)) # c1=consumer('lily')
# c1.__next__()
#
# c1.send('韭菜') def producer(name):
c=consumer('A')
c2=consumer('B')
c.__next__()
c2.__next__()
print('%s开始做包子了'%name)
for i in range(10):
time.sleep(1)
print('做了两个包子')
c.send(i)
c2.send(i) producer('Tom')
通过yield实现在单线程的实现并发运算
#yield相当于return控制的是函数的返回值
#x=yield的另外一个特性是,接收send()传过来的值赋值给yield def func():
print("yield来啦") name=yield print("yield接收send传参-%s"%name)
yield 2
print("下一步yield执行啦") test=func() print(test.__next__()) # 这一步打印的yield返回值是None res=test.send('Tom') #把Tom传给yield然后赋值给name,执行到yield 2返回 send相当于一次next操作 print(res)
yield-send()方法
三、装饰器
定义:装饰器本质上是一个函数
功能:就是为其他函数添加附加功能
原则:1.不能修改被装饰的函数的源代码
2.不能修改被装饰的函数的调用方式
#实现装饰器的知识: #1.函数即变量 #2.高阶函数:满足下面两个条件 #a: 把一个函数名当做实参传给另外一个函数 (在不修改被装饰函数源代码的情况下为其添加功能) #b: 返回值中包含函数名(不改变函数的调用方式) #3.嵌套函数 #高阶函数+嵌套函数+闭包 =装饰器
import time def timer(func):
def deco(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs) #会往上一级找
end_time=time.time()
print('the func run time is %s'%(end_time-start_time))
return deco
@timer
def test1(name):
time.sleep(3)
print('in the test1')
print(name)
@timer
def test2():
time.sleep(3)
print('in the test2') #test1=timer(test1)
#test2=timer(test2) test1('sunhao')
test2()
装饰器例子
import time def timmer(func):
def wrapper():
start_time=time.time()
result=func() #拿到func函数的返回值
stop_time=time.time()
print('the func run time is %s'%(stop_time-start_time))
return result #再返回
return wrapper def test():
time.sleep(1)
print('in the test1') res=timmer(test) #返回的是wrapper的地址 timmer(test) 相当于@timmer res() #执行的是wrapper函数 #把timmer(test)赋值给test,再执行test(),就没修改被装饰的函数的调用方式
import time
def timmer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper @timmer #相当于foo=timmer(foo)
def foo():
time.sleep(3)
print('from foo') foo()
无参装饰器
user_list=[{'name':'allen','password':''},
{'name':'lucy','password':''},
{'name':'lily','password':''}
] current_user={'username':None,'login':False} def auth_type(func): def wrapper(*args,**kwargs): if current_user['username'] and current_user['login']: ret = func(*args,**kwargs)
return ret username=input("请输入用户名:").strip()
password=input("请输入密码:").strip()
for name in user_list: if name['name']== username and name['password'] == password:
current_user['username']=username
current_user['login']=True ret = func(*args, **kwargs)
return ret else:
print("用户名密码错误") return wrapper @auth_type
def index(name):
print("欢迎来%s购物商城"%name) @auth_type
def shopping(): print("shopping 吧") index('京东')
shopping()
登录认证模拟session
user_list=[{'name':'allen','password':''},
{'name':'lucy','password':''},
{'name':'lily','password':''}
] current_user={'username':None,'login':False} def outer(auth_type): def auth_func(func): def wrapper(*args,**kwargs):
print("认证类型是%s"%auth_type) if auth_type =="file":
if current_user['username'] and current_user['login']: ret = func(*args,**kwargs)
return ret username=input("请输入用户名:").strip()
password=input("请输入密码:").strip()
for name in user_list: if name['name']== username and name['password'] == password:
current_user['username']=username
current_user['login']=True ret = func(*args, **kwargs)
return ret else:
print("用户名密码错误") elif auth_type =='mysql': ret = func(*args, **kwargs)
return ret return wrapper return auth_func @outer(auth_type='file') #auth_func=outer(auth_type='filedb')
def index(name):
print("欢迎来%s购物商城"%name) @outer(auth_type='mysql')
def shopping(): print("shopping 吧") index('京东')
shopping()
有参装饰器
Python全栈开发之4、迭代器、生成器、装饰器的更多相关文章
- 战争热诚的python全栈开发之路
从学习python开始,一直是自己摸索,但是时间不等人啊,所以自己为了节省时间,决定报个班系统学习,下面整理的文章都是自己学习后,认为重要的需要弄懂的知识点,做出链接,一方面是为了自己找的话方便,一方 ...
- python全栈开发之路
一.Python基础 python简介 python数据类型(数字\字符串\列表) python数据类型(元组\字典) python数据类型(集合) python占位符%s,%d,%r,%f prin ...
- python全栈开发之OS模块的总结
OS模块 1. os.name() 获取当前的系统 2.os.getcwd #获取当前的工作目录 import os cwd=os.getcwd() # dir=os.listdi ...
- Python全栈开发之3、深浅拷贝、变量和函数、递归、函数式编程、内置函数
一.深浅拷贝 1.数字和字符串 对于 数字 和 字符串 而言,赋值.浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址. import copy # 定义变量 数字.字符串 # n1 = 123 n1 ...
- Python全栈开发之MySQL(二)------navicate和python操作MySQL
一:Navicate的安装 1.什么是navicate? Navicat是一套快速.可靠并价格相宜的数据库管理工具,专为简化数据库的管理及降低系统管理成本而设.它的设计符合数据库管理员.开发人员及中小 ...
- Python全栈开发之14、Javascript
一.简介 前面我们学习了html和css,但是我们写的网页不能动起来,如果我们需要网页出现各种效果,那么我们就要学习一门新的语言了,那就是JavaScript,JavaScript是世界上最流行的脚本 ...
- Python全栈开发之1、输入输出与流程控制
Python简介 python是吉多·范罗苏姆发明的一种面向对象的脚本语言,可能有些人不知道面向对象和脚本具体是什么意思,但是对于一个初学者来说,现在并不需要明白.大家都知道,当下全栈工程师的概念很火 ...
- Python全栈开发之8、装饰器详解
一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了.转载请注明出处http://www.cnblogs.com/Wxtrkbc/p/5486253.html 一.装饰器 装饰器可以使函数执 ...
- Python全栈开发之7、面向对象编程进阶-类属性和方法、异常处理和反射
一.类的属性 1.@property属性 作用就是通过@property把一个方法变成一个静态属性 class Room: def __init__(self,name,length,width,he ...
随机推荐
- ubuntu系统火狐无法播放网页视频
在ubuntu开发环境下,浏览firefox上网页视频时,无法播放视频. 需要安装flash插件 命令如下: 1. sudo apt-get update 2. sudo apt-get in ...
- asp.net上传大文件的解决方案
IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下载请求时,服务 ...
- js+下载文件夹
一.此方法火狐有些版本是不支持的 window.location.href = 'https://*****.oss-cn-**.aliyuncs.com/*********'; 二.为了解决火狐有些 ...
- 清北学堂-贪心-bfs
输入样例: 3 5 10 5 4 10 8 1 10 1 3 1 4 1 5 1 3 2 1 2 5 4 3 4 3 4 5 5 1 1 4 4 6 1 9 4 7 2 9 5 10 5 2 8 8 ...
- POJ - 3376 Finding Palindromes manacher+字典树
题意 给n个字符串,两两拼接,问拼接后的\(n\times n\)个字符串中有多少个回文串. 分析 将所有正串插入字典树中,马拉车跑出所有串哪些前缀和后缀为回文串,记录位置,用反串去字典树中查询,两字 ...
- 5.13T1Send 题(send)
Send 题(send) [题目描述] 某个国家有
- Rhel7.4系统部署cobbler
cobbler安装 一.系统信息: [root@openstack ~]# cat /etc/redhat-release Red Hat Enterprise Linux Server releas ...
- 【洛谷1345】 [USACO5.4]奶牛的电信(最小割)
传送门 洛谷 Solution emmm,直接对于每一个点拆点就好了. 然后边连Inf,点连1,跑最小割就是答案. 代码实现 #include<bits/stdc++.h> using n ...
- async for的使用
import random import asyncio async def random_number_gen(delay, start, end): while True: yield rando ...
- Download google drive public shared file in terminal
http://unix.stackexchange.com/questions/136371/how-to-download-a-folder-from-google-drive-using-term ...