本节内容
1、迭代器&生成器
2、装饰器
3、Json & pickle 数据序列化
4、软件目录结构规范
5、作业:ATM项目开发
1、装饰器 decorator
定义:本质是函数,功能是用来装饰其他函数,就是为其他函数添加附加功能
原则:
1、不能修改被修饰函数的源代码
2、不能修改被装饰函数的调用方式
实现装饰器的知识储备:
1、函数即“变量”
2、高阶函数
满足以下一个条件即可
a、把一个函数名当实参传给另一个函数
b、返回值中包含函数名
3、嵌套函数
高阶函数+嵌套函数->装饰器
函数即“变量”:函数可以理解为和变量一样的概念,在内存中均是通过定义内存及对应的内存地址来存储和调用
高阶函数(条件a)
1 def test(func): 2 print(func) 3 func() 4 5 def bar(): 6 print('in the bar') 7 8 test(bar) 9 10 #执行结果 11 <function bar at 0x10e042158> #代表bar()的内存地址 12 in the bar
改动该高阶函数
1 import time 2 3 def test(func): 4 staart_time=time.time()#获取当前时间 5 func() 6 stop_time=time.time() 7 print('the running time of bar is %s' %(stop_time-staart_time)) 8 9 def bar(): 10 time.sleep(3) #睡眠3秒 11 print('in the bar') 12 13 test(bar) 14 15 #result 16 in the bar 17 the running time of bar is 3.000331163406372
其中bar()为源函数,test()为装饰函数,但是存在方法改变了bar()函数的调用方式,原本的调用方式是:bar(),现在需要运行装饰函数,则需将调用函数变更为:test(bar)
高阶函数(条件b)
1 import time 2 3 def test2(func): #装饰函数 4 print('in the test2') 5 return func 6 7 def orl(): #源函数 8 time.sleep(3) 9 print('in the orl') 10 11 orl=test2(orl) #将返回函数的内存地址重新赋值给orl 12 orl() #不改变源函数的调用方式 13 14 #result 15 in the test2 16 in the orl
嵌套函数
1 def foo(): 2 print('in the foo') 3 def bar(): #嵌套函数 4 print('in the bar') 5 bar() 6 7 foo() 8 9 10 #result 11 in the foo 12 in the bar
嵌套函数作用域
1 x=0 2 def grandpa(): 3 x=1 4 def dad(): 5 x=2 6 def son(): 7 x=3 8 print('son',x) 9 son() 10 print('dad',x) 11 dad() 12 print('grandpa',x) 13 14 grandpa() 15 16 #result 17 son 3 18 dad 2 19 grandpa 1
装饰器初现端倪
1 ''' 2 需求: 3 1、新增个装饰器,统计已存在的两个函数的运行时间 4 2、不能改变原有函数和原有调用方式 5 ''' 6 7 import time 8 9 def timer(func): #装饰器 10 def run_time(): 11 start_time=time.time() 12 func() #运行父级函数中获取的行参函数(嵌套函数) 13 stop_time=time.time() 14 print('the running time of func is %s' %(stop_time-start_time)) 15 return run_time #返回函数 16 17 def test1(): 18 time.sleep(2) 19 print('in the test1') 20 21 def test2(): 22 time.sleep(4) 23 print('in the test2') 24 25 test1=timer(test1) #将函数的内存地址获取,此时test1获取的时run_time的内存地址 26 test1() 27 28 test2=timer(test2) 29 test2() 30 31 #result 32 in the test1 33 the running time of func is 2.0049290657043457 34 in the test2 35 the running time of func is 4.002476930618286
伤痛:每次都需要将test1=timer(test1)函数再重新写一下,是否可以简化操作???这个必须有...
改良版的装饰器
1 ''' 2 需求: 3 1、新增个装饰器,统计已存在的两个函数的运行时间 4 2、不能改变原有函数和原有调用方式 5 ''' 6 7 import time 8 9 def timer(func): #装饰器 10 def run_time(): 11 start_time=time.time() 12 func() #运行父级函数中获取的行参函数(嵌套函数) 13 stop_time=time.time() 14 print('the running time of func is %s' %(stop_time-start_time)) 15 return run_time #返回函数 16 17 @timer #相当于test1=timer(test1) 18 def test1(): 19 time.sleep(2) 20 print('in the test1') 21 22 @timer #相当于test2=timer(test2) 23 def test2(): 24 time.sleep(4) 25 print('in the test2') 26 27 #test1=timer(test1) #将函数的内存地址获取,此时test1获取的时run_time的内存地址 28 test1() 29 30 #test2=timer(test2) 31 test2() 32 33 34 #result 35 in the test1 36 the running time of func is 2.0053460597991943 37 in the test2 38 the running time of func is 4.003633260726929
在原函数头部添加 @装饰器函数名称 来调用装饰器函数
那,如果有100个需要装饰的原函数,是否就需要写100次 @XXX??? 答:是的...吐一口老血
那,你的test1和test2都是没有参数的函数,实际中很多都是存在参数的函数,存在参数的函数,这个装饰器可以用吗?答:貌似会报错耶...再吐一口老血
有招治参数这个问题没?答:这个必须有
且看,通用装饰器
1 def timer(func): #装饰器 2 def run_time(*args,**kwargs): #使用不确定参数 3 start_time=time.time() 4 func(*args,**kwargs) #运行父级函数中获取的行参函数(嵌套函数) 5 stop_time=time.time() 6 print('the running time of func is %s' %(stop_time-start_time)) 7 return run_time #返回函数 8 9 @timer #相当于test1=timer(test1) 10 def test1(): 11 time.sleep(2) 12 print('in the test1') 13 14 @timer #相当于test2=timer(test2) 15 def test2(name1,name2): 16 time.sleep(4) 17 print('in the test2,name:',name1,name2) 18 19 test1() 20 test2('shang','ace') 21 22 23 #result 24 in the test1 25 the running time of func is 2.0028507709503174 26 in the test2,name: shang ace 27 the running time of func is 4.004335165023804
ps:在没听课件老师的讲解之前,我自己写出来的,大写的流弊啊,机智一匹....
少侠,要不要搞一场景展示下修炼成果?答:必须的嘛,对于我这么低调的人!
装饰器小试牛刀
需求如下:
需求: 1、模拟网页浏览 2、假设目前网站上有三个页面均不需要验证登录态 现在调整为 1、首页index不需要校验登录 2、home和trade页面需要校验登录 3、登录用户的用户名和密码数据从字典UserTable中获取 4、不改变原函数、调用方式、返回结果等信息 5、home和trade页面,一旦用户登录成功,则无需再登录
ps:话说老师给的需求太低,自己给自己加量,要不怎么能显示自己机智一匹呢~
话说,自己又写出来了,今天是怎么了,我竟如此机智!!!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 UserTable={ 2 'bilian':'111', 3 'shang':'123', 4 'ace':'456', 5 } 6 7 status={ 8 'cookie':0 #记录登录态 0代表未登录 1代表登录 9 } 10 11 def logger(msg): 12 print('logger:',msg) 13 14 15 #添加装饰器 16 def decorator(func): 17 def login(*args,**kwargs): 18 if status['cookie']==0: 19 username=input('Username:') 20 password=input('Password:') 21 if username in UserTable: 22 if password==UserTable[username]: 23 status['cookie']=1 24 logger('welcome back') 25 func(*args,**kwargs) 26 else: 27 logger('wrong password') 28 exit() 29 else: 30 logger('invalid username') 31 exit() 32 else: 33 logger('you have landed') 34 func(*args,**kwargs) 35 return login 36 37 38 #使用每一个函数代表调用每一个页面 39 def index(): 40 print('welcome to index page') 41 42 @decorator 43 def home(): 44 print('welcome to home page') 45 46 @decorator 47 def trade(): 48 print('welcome to trade page') 49 50 index() 51 home() 52 trade()
咦,老师让在原函数中添加返回值,这个我搞一搞试一试撒。
home原函数中改动:return '宝儿姐语录:我是台湾来的交换生'--脑补这个场景,宝儿姐老牛逼了
result:卧擦,加完装饰器之后,打印home()竟然直接返回None,这个是什么鬼~~~
老师支招,添加接收项,然后return即可。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 ''' 2 需求: 3 1、模拟网页浏览 4 2、假设目前网站上有三个页面均不需要验证登录态 5 现在调整为 6 1、首页index不需要校验登录 7 2、home和trade页面需要校验登录 8 3、登录用户的用户名和密码数据从字典UserTable中获取 9 4、不改变原函数、调用方式、返回结果等信息 10 5、home和trade页面,一旦用户登录成功,则无需再登录 11 ''' 12 13 UserTable={ 14 'bilian':'111', 15 'shang':'123', 16 'ace':'456', 17 } 18 19 status={ 20 'cookie':0 #记录登录态 0代表未登录 1代表登录 21 } 22 23 def logger(msg): 24 print('logger:',msg) 25 26 27 #添加装饰器 28 def decorator(func): 29 def login(*args,**kwargs): 30 if status['cookie']==0: 31 username=input('Username:') 32 password=input('Password:') 33 if username in UserTable: 34 if password==UserTable[username]: 35 status['cookie']=1 36 logger('welcome back') 37 result=func(*args,**kwargs) #用res来接收执行结果 38 return result #返回认证结果 39 else: 40 logger('wrong password') 41 exit() 42 else: 43 logger('invalid username') 44 exit() 45 else: 46 logger('you have landed') 47 func(*args,**kwargs) 48 return login 49 50 51 #使用每一个函数代表调用每一个页面 52 def index(): 53 print('welcome to index page') 54 55 @decorator 56 def home(): 57 print('welcome to home page') 58 return '宝儿姐语录:我是台湾来的交换生!' 59 60 @decorator 61 def trade(): 62 print('welcome to trade page') 63 64 index() 65 print(home()) 66 trade()
如果不同场景,需要装饰根据传递的参数确定执行的不同逻辑,此处应该如何处理?
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
''' 需求: 1、模拟网页浏览 2、假设目前网站上有三个页面均不需要验证登录态 现在调整为 1、首页index不需要校验登录 2、home和trade页面需要校验登录 3、登录用户的用户名和密码数据从字典UserTable中获取 4、不改变原函数、调用方式、返回结果等信息 5、home和trade页面,一旦用户登录成功,则无需再登录 ''' UserTable={ 'bilian':'111', 'shang':'123', 'ace':'456', } status={ 'cookie':0 #记录登录态 0代表未登录 1代表登录 } def logger(msg): print('logger:',msg) #添加装饰器 def decorator(login_type): print('deco login_type:',login_type) def LoginType(func): def login(*args,**kwargs): if status['cookie']==0: username = input('Username:') password = input('Password:') if login_type=='local': if username in UserTable: if password==UserTable[username]: #status['cookie']=1 执行trade()函数时,需要重新进行下用户名和密码的输入,已确定可以执行WeChat的逻辑 logger('welcome back') result=func(*args,**kwargs) #用res来接收执行结果 return result #返回认证结果 else: logger('wrong password') exit() else: logger('invalid username') exit() elif login_type=='WeChat': print('WeChat API Login') exit() else: logger('you have landed') func(*args,**kwargs) return login return LoginType #使用每一个函数代表调用每一个页面 def index(): print('welcome to index page') #home使用本地用户数据进行校验 @decorator(login_type='local') def home(): print('welcome to home page') return '宝儿姐语录:我是台湾来的交换生!' #trade使用第三方微信的账户信息进行校验 @decorator(login_type='WeChat') def trade(): print('welcome to trade page') index() print(home()) trade()
备注:
1、在装饰器调用函数上增加参数,用于区分不同的场景,@decorator(login_type='local')
2、在装饰器调用时,先透传login_type参数,所以在最外层的decorator()需要添加参数,用以接收设置的参数,def decorator(login_type)
3、需要再新建一层嵌套函数,用以接受原来的func函数的高阶函数,LoginType(func),并且需要新增该函数的返回return LoginType
4、在程序里面校验login_type的类型,在执行trade函数时,需要重新在设置下用户名和密码,以便可以执行login_type的判断
ps:老师讲的已经完全高潮了,但是宝宝内心只是大概知道其中的逻辑,具体函数执行的顺序,已经夜深头昏脑胀的搞不明白,以后再慢慢梳理吧...
2、迭代器
列表生成式
1 # 列表生成式 2 a=[ i*2 for i in range(10) ] 3 print(a) 4 5 6 #result 7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
其中a=[ i*2 for i in range(10) ]就是列表生成器
列表生成器相当于如下代码:
1 a=[] #声明一个空的列表 2 for i in range(10): 3 a.append(i*2) #给列表添加值 4 print(a)
当然,也有高bigger的操作~
1 # 高级操作 2 def func(i): 3 data=i*i 4 return data 5 arr=[ func(i) for i in range(10) ] 6 print(arr) 7 8 #result 9 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
其中,将i替换成func函数的形式,666...
- 生成器
摘录金角大王:
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
ps:嗯嗯,好有道理的样子...不过,如果这样的话,那为什么还要用列表呢?是否可以def一个函数来代表该算法,每次调用的时候,直接计算出来即可,这个不就解决这个困境了吗???
创建生成器:
(i*2 for i in range(10))
定义该生成器,在内存中只是对一个内存地址,其实并没有生成相应的各个元素
打印该生成器,如下:
1 c=(i*2 for i in range(10)) 2 print(c) 3 4 #result 5 <generator object <genexpr> at 0x1007f59a8> #生成器对应的内存地址
没办法直接查询生成器指定角标对应的参数,必须一个一个参数的依次生成,然后读取
1 c=(i*2 for i in range(10)) 2 print(c) 3 print(c.__next__()) 4 for i in range(8): 5 print(c.__next__()) 6 7 #result 8 <generator object <genexpr> at 0x10de289a8> 9 0 #第一次读取的c中的参数 10 2 #循环获取的第一个参数 11 4 12 6 13 8 14 10 15 12 16 14 17 16
备注:生成器采用c.__next__()的方式生成并获取相应的参数
斐波那契数列:1、1、2、3、5、8、13、21、34、……F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
老师给的代码:
1 def fib(num): 2 n,a,b=0,0,1 3 while n<num: 4 print(b) 5 a,b=b,a+b 6 n=n+1 7 8 fib(10) #打印斐波那契数列的前10个参数 9 10 #result 11 1 12 1 13 2 14 3 15 5 16 8 17 13 18 21 19 34 20 55
原本以为n,a,b=0,0,1是n=0,a=0,b=1,以及a,b=b,a+b是a=b,b=a+b的含义,可是,调整代码后执行结果,很是让宝宝吃惊....
def fib(num): #n,a,b=0,0,1 n=0 a=0 b=1 while n<num: print(b) #a,b=b,a+b a=b b=a+b n=n+1 fib(10) #result 1 2 4 8 16 32 64 128 256 512
这是在做一个累加的数列,完全不是斐波那契数列,且听老师讲解。
老师讲解a,b=b,a+b不是a=b,b=a+b的含义,是元组的概念,代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def fib(num): 2 #n,a,b=0,0,1 3 t1 = (0, 0, 1) 4 n = t1[0] 5 a = t1[1] 6 b = t1[2] 7 while n<num: 8 print(b) 9 #a,b=b,a+b 10 t2 = (b, a + b) 11 a = t2[0] 12 b = t2[1] 13 n=n+1 14 15 fib(10) #打印斐波那契数列的前10个参数 16 17 #result 18 1 19 1 20 2 21 3 22 5 23 8 24 13 25 21 26 34 27 55
备注:其中t2是一个动态元组,每次循环,对应的t2中元素均一直在变化;
ps:看着很有道理的样子,但是感觉这种完全可以有其他替代方式,我自己搞一发试一下->_->
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 ''' 2 斐波那契函数的数学运算符 3 F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*) 4 ''' 5 6 #哥就是不服 7 def fib(num): 8 a=[] 9 for i in range(num): 10 if i==0: 11 a.append(1) 12 elif i==1: 13 a.append(1) 14 else: 15 a.append(a[i-1]+a[i-2]) 16 return a 17 18 print(fib(10)) 19 20 #result 21 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
我去,叼叼叼,自己搞出来的感觉就是爽....
老师给的斐波那契代码,稍微调整一下即可生成相应的generator,要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了
1 def fib(num): 2 n,a,b=0,0,1 3 while n<num: 4 #print(b) 5 yield b #将函数改成生成器 6 a,b=b,a+b 7 n=n+1 8 9 f=fib(10) #打印斐波那契数列的前10个参数 10 print(f.__next__()) 11 print('-----我是分隔符-----') 12 print(f.__next__()) 13 print(f.__next__()) 14 print('调用另一个程序执行相应逻辑') 15 print(f.__next__()) 16 print(f.__next__()) 17 18 #result 19 1 20 -----我是分隔符----- 21 1 22 2 23 调用另一个程序执行相应逻辑 24 3 25 5
这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator:
1 def fib(num): 2 n,a,b=0,0,1 3 while n<num: 4 #print(b) 5 yield b #将函数改成生成器 6 a,b=b,a+b 7 n=n+1 8 9 f=fib(10) #打印斐波那契数列的前10个参数 10 print(f) 11 12 #result 13 <generator object fib at 0x105a759a8>
这里,最难理解的就是generator和函数的执行流程不一样。
函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。在上面fib
的例子,我们在循环过程中不断调用yield
,就会不断中断。
通常,把函数改成generator后,我们基本上从来不会用next()
来获取下一个返回值,而是直接使用for
循环来迭代:
1 def fib(num): 2 n,a,b=0,0,1 3 while n<num: 4 #print(b) 5 yield b #将函数改成生成器 6 a,b=b,a+b 7 n=n+1 8 9 f=fib(10) #打印斐波那契数列的前10个参数 10 print(f) 11 12 for i in range(10): 13 print(f.__next__()) 14 15 #result 16 <generator object fib at 0x101ac59a8> 17 1 18 1 19 2 20 3 21 5 22 8 23 13 24 21 25 34 26 55
但是用for
循环调用generator时,发现拿不到generator的return
语句的返回值,如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def fib(num): 2 n,a,b=0,0,1 3 while n<num: 4 #print(b) 5 yield b #将函数改成生成器 6 a,b=b,a+b 7 n=n+1 8 return '已撸完' 9 10 f=fib(10) #打印斐波那契数列的前10个参数 11 12 #采用异常处理,用于抓取return结果 13 while True: 14 try: 15 x = next(f) 16 print('f:', x) 17 except StopIteration as e: 18 print('Generator return value:', e.value) 19 break 20 21 #result 22 f: 1 23 f: 1 24 f: 2 25 f: 3 26 f: 5 27 f: 8 28 f: 13 29 f: 21 30 f: 34 31 f: 55 32 Generator return value: 已撸完
备注:其中循环执行完成之后,通过抓取报错信息的机制,获取到return的内容
- 生成器的运用
可通过yield实现在单线程的情况下实现并发运算的效果
#生成器并行 import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield #在consumer中只是中断标示 print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer('A') c2 = consumer('B') c.__next__() #consumer运行到yield处返回 c2.__next__() print("老子开始准备做包子啦!") for i in range(10): time.sleep(1) print("做了2个包子!") c.send(i) #将i的value透传至yield赋值参数 c2.send(i) producer("alex")
备注:c = consumer('A'),只是生成该迭代器,需要用c.__next__()开始执行生成器
3、迭代器
可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以使用isinstance()
判断一个对象是否是Iterable:
1 from collections.abc import Iterable 2 3 #列表是可以迭代的 4 print(isinstance([],Iterable)) 5 6 #字典是可以迭代的 7 print(isinstance({}, Iterable)) 8 9 #字符串是可以迭代的 10 print(isinstance('abc', Iterable)) 11 12 #for循环可以迭代的 13 print(isinstance((x for x in range(10)), Iterable)) 14 15 #数字不可以迭代的 16 print(isinstance(100, Iterable)) 17 18 #result 19 True 20 True 21 True 22 True 23 False
而生成器不但可以作用于for
循环,还可以被next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了。
*可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
可以使用isinstance()
判断一个对象是否是Iterator
对象:
1 from collections.abc import Iterator 2 3 # 列表不是迭代器 4 print(isinstance([],Iterator)) 5 6 #字典不是迭代器 7 print(isinstance({}, Iterator)) 8 9 #字符串不是迭代器 10 print(isinstance('abc',Iterator)) 11 12 #for循环是迭代器 13 print((x for x in range(10)), Iterator) 14 15 16 #result 17 False 18 False 19 False 20 <generator object <genexpr> at 0x108d3da98> <class 'collections.abc.Iterator'>
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
1 from collections.abc import Iterator 2 print(isinstance(iter([]), Iterator)) 3 print(isinstance(iter('abc'), Iterator)) 4 print(isinstance(iter({}), Iterator)) 5 6 #result 7 True 8 True 9 True
例如,将列表a转化成迭代器
1 a=[1,2,3] 2 a1=iter(a) 3 print(a1.__next__()) 4 print(a1.__next__()) 5 print(a1.__next__()) 6 print(a1.__next__()) 7 8 #result 9 1 10 2 11 3 12 Traceback (most recent call last): 13 File "/Users/shang/PycharmProjects/AceExercise/day4/Iterator.py", line 46, in <module> 14 print(a1.__next__()) 15 StopIteration
内心小疑问:为什么list
、dict
、str
等数据类型不是Iterator
?
老实说:这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结:
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
1 for x in [1,2,3,4]: 2 print(x)
实际上完全等价于:
1 # 首先获得Iterator对象: 2 it = iter([1, 2, 3, 4, 5]) 3 # 循环: 4 while True: 5 try: 6 # 获得下一个值: 7 x = next(it) 8 print(x) 9 except StopIteration: 10 # 遇到StopIteration就退出循环 11 break
4、内置参数
内置参数详解:https://docs.python.org/3/library/functions.html?highlight=built#ascii
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #内置参数 2 3 #绝对值abs(x) 4 print(abs(-1)) 5 6 #all(iterable) 7 #非0则为真 8 print(all([1,-2,3])) 9 print(all([0])) 10 11 #any(iterable) 12 #只要非空和0则为真 13 print(any([])) 14 print(any([0])) 15 print(any([1,2])) 16 17 #ascii(object) 18 #没什么用,直接忽视 19 20 #bin(x) 21 print(bin(4)) 22 #把数字转成二进制,用0b开头 23 24 #class bool([x]) 布尔值,真为true,假为false 25 print(bool(0)) 26 27 28 #class bytearray([source[, encoding[, errors]]]) 29 a=bytes('abcde',encoding='utf-8') 30 print(a) #打印字节格式 31 print(a.capitalize(),a) #a.capitalize() 相当于重新生成一个新的二进制字节 32 #字符串不可以修改,二进制的字节格式更不可以修改 33 b=bytearray('abcde',encoding='utf-8') 34 print(b[0]) #打印a的ascii值 35 b[0]=98 #将a的调整城b,赋值必须输入的是ascii值 36 print(b) #改变原二进制的内容 37 38 39 #callable(object) 是否可调用的意思 40 print(callable([])) #[]不可以被调用 41 #可以加()都是可以调用的 42 def func(): pass 43 print(callable(func)) #函数可以被调用 44 45 46 # chr(i) 该ascii值对应的字符 47 print(chr(97)) 48 #ord(i) 该字符对应的ascii值 49 print(ord('a')) 50 51 52 #@classmethod 后期再展开 53 54 #compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) 55 code='for i in range(10):print(i)' 56 c=compile(code,'','exec') 57 exec(c) 58 code2='1+3*10/2' 59 d=compile(code2,'','eval') 60 print(eval(code2)) #可以执行code 61 print(eval(d)) 62 #该方式相当于import,将外部函数使用字符串的形式传递给code,然后编译并执行code 63 code=''' 64 def fib(num): 65 n,a,b=0,0,1 66 while n<num: 67 print(b) 68 a,b=b,a+b 69 n=n+1 70 return '已撸完' 71 72 f=fib(10) 73 ''' 74 exec(code) #可以不进行编译,直接执行 75 76 #class complex([real[, imag]]) 复数 77 78 #delattr(object, name) 面向对象时再展开 79 80 #dir([object]) 81 arr=[] 82 print(dir(arr)) #打印列表可用的方法 83 84 #divmod(a, b) 85 print(divmod(5,3)) #(1,2) 1为余数,2为商 86 87 # filter(function, iterable) 88 #前置:匿名函数 89 cal=lambda i:print(i) 90 cal(5) 91 cal2=lambda n:3 if n<4 else n 92 print(cal2(6)) 93 #匿名函数只能处理三元运算,不能处理复杂的运算 94 #filter可以从一组数据中过滤出想要的数据 95 data=filter(lambda n:n>5,range(10)) #data为迭代器 96 for i in data: 97 print(i) 98 99 #map 将参数按照计算规则进行运算 100 data2=map(lambda n:n*n,range(10)) 101 for i in data2: 102 print(i) 103 104 #reduce 105 import functools 106 data3=functools.reduce(lambda x,y:x+y,range(10)) 107 print(data3) #将参数循环运算,此处时1+2+3+...+10赋值给data3 108 109 # class frozenset([iterable]) 110 # 将集合变为不可编辑的状态 111 112 #getattr(object, name[, default]) 后期再展开 113 print(globals()) #当前文件所有变量key-value的字典 114 115 #hex(x) 116 #将数字转换成16进制 117 print(hex(15)) 118 119 #oct 将数据转换成8进制 120 print(oct(8)) 121 122 #len(s) 长度 123 data4=[1,2,3,4] 124 print(len(data4)) 125 126 #round 精度,默认精度为整数 127 print(round(3.1415926)) 128 print(round(3.1415926,4)) 129 130 dict2={1:2,3:5,10:-3,-5:22,-10:1} 131 print(sorted(dict2.items()))#默认按照key从小到大,进行排序 132 print(sorted(dict2.items(),key=lambda x:x[1])) #按照value的从小到大排序 133 134 135 #zip 组合 136 data5=[1,2,3,4,5] 137 data6=['a','b','c','d'] 138 for i in zip(data5,data6): 139 print(i) #按照数据少的数组来组合
5、json & pickle 模块
用于序列化的两个模块
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
Json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
需求:
当特定时间节点时,需将系统相关信息进行镜像,此时将相关的信息保存成Json的格式;当需要系统镜像信息进行重新读取和使用的时,可以实现正常获取
使用josn的序列化和反序列操作
- json序列化
1 import json #引用json模块 2 3 info={ 4 'name':'Ace', 5 'age':29 6 } 7 8 f=open('Json.text','w') 9 f.write(json.dumps(info)) #序列化使用dumps 10 f.close() 11 12 #result 13 #创建json.text文件,并将info信息以字符串形式存储
- json反序列化
1 import json 2 3 f=open('Json.text','r') 4 data=json.loads(f.read()) 5 print(data['age']) 6 7 #result 8 29
json是用来不同程序之间的数据交互
json将逐渐取代xml进行数据交互主流方式
但是json无法处理复杂的功能,例如:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import json #引用json模块 2 3 def sayhi(name): 4 print(name) 5 6 info={ 7 'name':'Ace', 8 'age':29, 9 'func':sayhi 10 } 11 12 #序列化 13 f=open('Json.text','w') 14 f.write(json.dumps(info)) #序列化使用dumps 15 f.close() 16 17 18 #反序列化 19 f=open('Json.text','r') 20 data=json.loads(f.read()) 21 print(data['age'])
运行代码时报错:Object of type function is not JSON serializable
为解决这个问题,需要使用pickle模块,但是java无法识别pickle的序列化
- pickle序列化和反序列化
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import pickle 2 3 def sayhi(name): 4 print(name) 5 6 info={ 7 'name':'Ace', 8 'age':29, 9 'func':sayhi 10 } 11 12 #序列化 13 f=open('Json.text','wb') #wb使用字节的方式进行操作 14 f.write(pickle.dumps(info)) #序列化使用dumps 15 f.close() 16 17 18 #反序列化 19 f=open('Json.text','rb') 20 data=pickle.loads(f.read()) 21 print(data['age']) 22 print(data['func']('Ace')) 23 24 25 #result 26 29 27 Ace 28 None
备注:其中pickle许列化的的数据格式为字节格式,另外,没明白sayhi函数执行完结果中None代表的什么含义??
dump和load的用法
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import pickle 2 3 def sayhi(name): 4 print(name) 5 6 info={ 7 'name':'Ace', 8 'age':29, 9 'func':sayhi 10 } 11 12 #序列化 13 f=open('Json.text','wb') #wb使用字节的方式进行操作 14 15 #f.write(pickle.dumps(info)) #序列化使用dumps 16 pickle.dump(info,f) 17 18 f.close() 19 20 21 #反序列化 22 f=open('Json.text','rb') 23 24 #data=pickle.loads(f.read()) 25 data=pickle.load(f) 26 27 print(data['age']) 28 print(data['func']('Ace'))
需求:多次挂起(序列化),多次读取(反序列化)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import json 2 3 info={ 4 'name':'Ace', 5 'age':29 6 } 7 8 #序列化 9 f=open('Json.text','w') 10 11 json.dump(info,f) 12 13 info['age']=30 14 json.dump(info,f) #实现二次序列化,在text文件中,新增另外记录 15 16 f.close()
python3.0是可以实现多次序列化
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import json 2 3 info={ 4 'name':'Ace', 5 'age':29 6 } 7 8 #反序列化 9 f=open('Json.text','r') 10 data=json.load(f) 11 print(data['age']) 12 13 f.close() 14 15 #result 16 程序报错,当多次序列化之后,无法再实现反序列化
结论:只能一次挂起+一次读取,不能连续多次挂起和多次读取