一、装饰器
一、装饰器的知识储备
不想修改函数的调用方式,但是还想在原来的函数前后添加功能
1、可变长参数 :*args和**kwargs
1 def index(name,age): 2 print(name,age) 3 4 def wrapper(*args,**kwargs): 5 #即args=(1,2,3,4,5),kwargs={'x':1,'y':3} 6 index(*args,**kwargs) 7 #index(1,2,3,4,5,y=2,x=5)
2、函数对象:被当做数据传递
1、函数可以当做参数传给另外一个函数 2、一个函数的返回值,也可以是一个函数(打破函数的层级关系) def f1(): def f2(): print('f2') return f2 ##打破函数的层级关系 f=f1() f()
3、名称空间和作用域
1、名称空间: 分类:分三种 内置名称空间:Python解释器启动则生效,关闭时失效 全局名称空间:执行Python文件时生效 内置名称空间:调用函数时,临时生效;函数调用结束失效 加载顺序:先内置,在全局,最后有可能产生局部 查找名字的顺序:先局部,再全局,最后内置 2、作用域 分类:分两种 全局作用域:全局存活,全局有效 局部作用域:临时存活,局部有效 强调:作用关系在函数定义阶段已经固定,与调用位置无关
二、闭包函数
1、定义
1、定义在函数内部的函数
2、包含对外部作用域名字的引用,而不是对全局作用域名字的引用
那么该内部函数称之为闭包函数
2、实例
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 x = 1 2 def f1(): 3 x=111111111111 4 def f2(): #f2是闭关函数 5 print(x) 6 return f2 ##获取返回值 7 func=f1() 8 # func() 9 10 def foo(): 11 x=1988193190112131 12 func() 13 foo()
3、应用:延迟计算/惰性计算(爬网页)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def get(url): 2 return requests.get(url).text 3 # print(get('https://www.toutiao.com/')) 4 print(get('https://www.python.org'))
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import requests #需要pip3 install request 2 def index(url): 3 # url='https://www.python.org' 4 def get(): 5 return requests.get(url).text 6 return get 7 python_web=index('https://www.python.org') 8 baidu_web=index('https://www.baidu.com') 9 python_web() 10 baidu_web()
from urllib.request import urlopen
def get(url): #url='http://www.baidu.com'
# url='http://www.baidu.com'
def inner():
return urlopen(url).read()
return inner
baidu=get('http://www.baidu.com')
print(baidu)
res=baidu()
baidu()
def index(url):
# url='https://www.python.org'
def warpper():
return requests.get(url).text
return warpper
python_web=index('https://www.python.org')
print(python_web.__closure__[0]) ##closure 闭包 ##能看到内存地址就不要使用ID
三、装饰器
装饰器就是闭包函数的一种应用场景
1、为何要用装饰器
开放封闭原则:对修改封闭,对扩展开放
2、装饰器的定义和原则
装饰器本身可以是任意可以调用对象,被装饰的对象本身也可以是任意可调用对象
定义:本质是函数,(装饰其他函数),就是为其他函数添加附加功能
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
原则:1、不能修改被装饰的函数的源代码
2、不能修改被装饰的函数的调用方式
添加统计执行时间的功能
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import time 2 def index(): 3 start=time.time() 4 time.sleep(3) 5 print('welcome to index') 6 stop=time.time() 7 print('run time is :[%s}' %(stop-start)) 8 index()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import time 2 def index(): 3 time.sleep(3) 4 print('welcome to index') 5 # index() 6 7 def wrapper(func): 8 start=time.time() 9 func() 10 stop=time.time() 11 print('run time is %s' %(stop-start)) 12 #wrapper(index) ##注意index一定不能加() ,因为使用的是内存地址 13 index=wrapper(index)
3、装饰的定义和调用
使用装饰器添加统计执行时间的功能,不修改原代码,不修改调用方式
1 import time 2 def timmer(func): 3 # func=index 4 def wrapper(): 5 start=time.time() 6 func() 7 stop=time.time() 8 print('run time is [%s]' %(stop-start)) 9 return wrapper 10 11 @timmer #等价于index=timmer(index) #@装饰器名,会将正下方函数名作为参数传给装饰器,然后重新赋值给函数名 12 def index(): 13 time.sleep(3) 14 print('welcome to index') 15 # index=timmer(index) ##实践一:重新赋值,然后调用 16 # index() 17 18 @timmer #等价于home=timmer(home) 19 def home(): 20 time.sleep(3) 21 print('welcome %s to home' %name) 22 index() 23 home()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/9/23 5 ##有参和无参函数都能使用装饰器(*args,**kwargs) 6 import time 7 def timmer(func): 8 # func=index 9 def wrapper(*args,**kwargs): 10 start=time.time() 11 res=func(*args,**kwargs) #有无返回值,均可处理:有返回值,是相应的返回值,没有返回值是None 12 stop=time.time() 13 print('run time is %s' %(stop-start)) 14 return res ##有无返回值,均可处理 15 return wrapper 16 @timmer #等价于index=timmer(index) #@装饰器名,会将下面函数名作为参数传给装饰器,然后重新赋值给函数名 17 def index(): 18 time.sleep(3) 19 print('welcome to index') 20 return 123 21 # index() 22 23 @timmer #等价于home=timmer(home) 24 def home(name): 25 time.sleep(3) 26 print('welcome to home') 27 #有返回值 28 res=index() #即res=wrapper 29 print(res) 30 home('wzs') #即wrapper('wzs')
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/9/23 5 ##eval 将字符串里面的命令提取出来,执行一下 6 #字符串转成字典 7 # dic='{"name":"alex","password":"alex123"}' 8 # d=eval(dic) 9 # print(type(d)) 10 11 ##从文件取用户信息进行认证 12 # with open('db.txt',encoding='utf-8') as f: 13 # data=f.read() 14 # dic=eval(data) 15 # print(dic['name']) 16 17 ##保存用户登录状态 18 current_user={'user':None,'current_status':False} 19 def auth(func): 20 def wrapper(*args,**kwargs): 21 if current_user['user'] and current_user['current_status']: 22 return func(*args,**kwargs) 23 name=input('please input your name:').strip() 24 password=input('please input your password:').strip() 25 26 ##用户的认证来源有多种:文件,数据库等等 27 with open('db.txt', encoding='utf-8') as f: 28 user_dic = eval(f.read()) 29 # if name == user_dic['name'] and password == user_dic['password']: 30 if name in user_dic and password == user_dic[name]: 31 res=func(*args,**kwargs) #有无返回值,均可处理:有返回值,是相应的返回值,没有返回值是None 32 current_user['user'] = name ##登录成功记录下来 33 current_user['current_status'] == True 34 return res ##有无返回值,均可处理 35 else: 36 print('user or password is wrong') 37 return wrapper 38 @auth #等价于index=timmer(index) #@装饰器名,会将下面函数名作为参数传给装饰器,然后重新赋值给函数名 39 def index(): 40 print('welcome to index') 41 return 123 42 # index() 43 44 @auth #等价于home=timmer(home) 45 def home(name): 46 print('welcome to home') 47 #有返回值 48 res=index() #即res=wrapper 49 print(res)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/9/23 5 ####使用装饰器 6 import time 7 from functools import wraps ##引用Python自带的装饰器 8 current_user={'user':None,'current_status':False} 9 def auth(auth_type='file'): 10 def deco(func): 11 def wrapper(*args,**kwargs): 12 if auth_type == 'file': 13 if current_user['user']: 14 return func(*args,**kwargs) 15 name=input('please input your name:').strip() 16 password=input('please input your password:').strip() 17 18 ##用户的认证来源有多种:文件,数据库等等 19 with open('db.txt', encoding='utf-8') as f: 20 user_dic = eval(f.read()) 21 # if name == user_dic['name'] and password == user_dic['password']: 22 if name in user_dic and password == user_dic[name]: 23 res=func(*args,**kwargs) #有无返回值,均可处理:有返回值,是相应的返回值,没有返回值是None 24 current_user['user'] = name ##登录成功记录下来 25 current_user['current_status'] = True 26 return res ##有无返回值,均可处理 27 else: 28 print('user or password is wrong') 29 elif auth_type == "mysql": 30 print('mysql') 31 elif auth_type == 'ldap': 32 print('ldap') 33 else: 34 print('not valid auth_type') 35 return wrapper 36 return deco 37 def timmer(func): 38 # func=index 39 @wraps(func) ##利用Python的自带的装饰器(可以查到注释信息) 40 def wrapper(): 41 start=time.time() 42 func() 43 stop=time.time() 44 print('run time is %s' %(stop-start)) 45 return wrapper 46 ##装饰器是有先后顺序的,装饰器装饰的是正下方的函数 47 ##上面装饰器先生效,下面的后生效;但是先执行下面(函数正上方的装饰器) 48 @timmer #index=timmer(wrapper) 49 @auth() # @deco #index=deco(index) #index=wrapper 50 def index(): 51 '''这是函数''' 52 time.sleep(3) 53 print('welcome to index') 54 # index() 55 56 @timmer #等价于home=timmer(home) 57 @auth() 58 def home(): 59 time.sleep(3) 60 print('welcome to home') 61 # index() 62 # home() 63 print(index.__doc__) ##加上装饰器后默认是返回None ;调用系统自带的装饰器from functools import wraps ,引用@wraps后,可以查看函数的注释信息 64 # print(help(index)) ##查看函数注释信息
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/9/23 5 6 current_user={'user':None,'current_status':False} 7 def auth(auth_type='file'): ##给装饰器传参数,最多三层(已经满足所有需求,一般情况下,直接调用别人的有参装饰器) 8 def deco(func): 9 def wrapper(*args,**kwargs): 10 if auth_type == 'file': 11 if current_user['user']: 12 return func(*args,**kwargs) 13 name=input('please input your name:').strip() 14 password=input('please input your password:').strip() 15 16 ##用户的认证来源有多种:文件,数据库等等 17 with open('db.txt', encoding='utf-8') as f: 18 user_dic = eval(f.read()) 19 # if name == user_dic['name'] and password == user_dic['password']: 20 if name in user_dic and password == user_dic[name]: 21 res=func(*args,**kwargs) #有无返回值,均可处理:有返回值,是相应的返回值,没有返回值是None 22 current_user['user'] = name ##登录成功记录下来 23 current_user['current_status'] = True 24 return res ##有无返回值,均可处理 25 else: 26 print('user or password is wrong') 27 elif auth_type == "mysql": 28 print('mysql') 29 elif auth_type == 'ldap': 30 print('ldap') 31 else: 32 print('not valid auth_type') 33 return wrapper 34 return deco 35 @auth(auth_type='mysql')#等价于@deco #index=deco(index) #index=inner #@装饰器名,会将下面函数名作为参数传给装饰器,然后重新赋值给函数名 36 def index(): 37 print('welcome to index') 38 return 123 39 # index() 40 41 @auth(auth_type='file') #等价于home=timmer(home) 42 def home(name): 43 print('welcome %s to home' %name) 44 #有返回值 45 res=index() #即res=wrapper 46 print(res) 47 home('alex')
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{"alex":"alex123","egon":"egon123","wzs":"wzs123"}
装饰器最多三层函数,三层几乎满足所有的需求了
4、练习题
一:编写函数,(函数执行的时间是随机的)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 import time 7 def timmer(func): 8 def wrapper(*args,**kwargs): 9 start = time.time() 10 func(*args,**kwargs) 11 stop = time.time() 12 print('execution time is %s' %(start)) 13 14 return wrapper 15 @timmer 16 def exec(): 17 print('what are you doing?') 18 exec()
二:编写装饰器,为函数加上统计时间的功能
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 import time 7 def timmer(func): 8 def wrapper(*args,**kwargs): 9 start = time.time() 10 func(*args,**kwargs) 11 stop = time.time() 12 print('execution time is %s' %(start)) 13 14 return wrapper 15 @timmer 16 def exec(): 17 print('what are you doing?') 18 exec()
三:编写装饰器,为函数加上认证的功能
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 def auth(func): 7 def wrapper(*args,**kwargs): 8 name = input('please your name>>:').strip() 9 password = input('please your password>>:').strip() 10 if name == 'wzs' and password == 'wzs123': 11 func(*args,**kwargs) 12 return wrapper 13 @auth 14 def login(name): 15 print('%s 欢迎登录' %(name)) 16 login('wzs')
四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 current_user={'user':None,'current_status':False} 7 def auth(func): 8 def wrapper(*args,**kwargs): 9 if current_user['user'] and current_user['current_status']: 10 return func(*args,**kwargs) 11 name=input('please input your name:').strip() 12 password=input('please input your password:').strip() 13 14 ##用户的认证来源有多种:文件,数据库等等 15 with open('db.txt', encoding='utf-8') as f: 16 user_dic = eval(f.read()) 17 # if name == user_dic['name'] and password == user_dic['password']: 18 if name in user_dic and password == user_dic[name]: 19 res=func(*args,**kwargs) #有无返回值,均可处理:有返回值,是相应的返回值,没有返回值是None 20 current_user['user'] = name ##登录成功记录下来 21 current_user['current_status'] == True 22 return res ##有无返回值,均可处理 23 else: 24 print('user or password is wrong') 25 return wrapper 26 @auth #等价于index=timmer(index) #@装饰器名,会将下面函数名作为参数传给装饰器,然后重新赋值给函数名 27 def index(): 28 print('welcome to index') 29 return 123 30 # index() 31 32 @auth #等价于home=timmer(home) 33 def home(name): 34 print('welcome to home') 35 #有返回值 36 res=index() #即res=wrapper 37 print(res)
五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 import time,random 7 user={'user':None,'login_time':None,'timeout':0.000005,} 8 def timmer(func): 9 def wrapper(*args,**kwargs): 10 exe_start = time.time() 11 res = func(*args,**kwargs) 12 exe_stop = time.time() 13 print('%s' %(exe_stop - exe_start)) 14 return res 15 return wrapper 16 17 def auth(func): 18 def wrapper(*args,**kwargs): 19 if user['user']: 20 timeout = time.time() - user['login_time'] 21 if timeout < user['timeout']: 22 return func(*args,**kwargs) 23 name = input('your name>>:').strip() 24 password = input('your password>>:').strip() 25 if name == 'wzs' and password == 'wzs123': 26 user['user'] = name 27 user['login_time'] = time.time() 28 res = func(*args,**kwargs) 29 return res 30 return wrapper 31 32 @auth 33 def index(): 34 time.sleep(random.randrange(3)) 35 print('welcome to index') 36 @auth 37 def home(name): 38 time.sleep(random.randrange(3)) 39 print('welcome %s to home' %name) 40 41 index() 42 home('wzs')
六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 import requests 7 def index(url): 8 def wrapper(): 9 return requests.get(url).text 10 return wrapper 11 12 index_web = index('https://www.python.org') 13 print(index_web())
七:为题目五编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 import requests,os 7 cache_file = 'cache.txt' 8 def make_cache(func): 9 def wrapper(*args,**kwargs): 10 if not os.path.exists(cache_file): 11 with open(cache_file,'w'):pass 12 if os.path.getsize(cache_file): 13 with open(cache_file,'r',encoding='utf-8') as f: 14 res = f.read() 15 else: 16 res = func(*args,**kwargs) 17 with open(cache_file,'w',encoding='utf-8') as f: 18 f.write(res) 19 return res 20 return wrapper 21 @make_cache 22 def get(url): 23 return requests.get(url).text 24 25 get('https://www.python.org')
八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 route_dic={} 7 8 def make_route(name): 9 def deco(func): 10 route_dic[name]=func 11 return deco 12 @make_route('select') 13 def func1(): 14 print('select') 15 16 @make_route('insert') 17 def func2(): 18 print('insert') 19 20 @make_route('update') 21 def func3(): 22 print('update') 23 24 @make_route('delete') 25 def func4(): 26 print('delete') 27 28 print(route_dic)
九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
注意:时间格式的获取
import time
time.strftime('%Y-%m-%d %X')
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/10/7 5 6 import time,os 7 def auth(logfile): 8 def deco(func): 9 if not os.path.exists(logfile): 10 with open(logfile,'w',encoding='utf-8') as f:pass 11 def wrapper(*args,**kwargs): 12 res = func(*args,**kwargs) 13 with open(logfile,'a',encoding='utf-8') as f: 14 f.write('%s %s run'%(time.strftime('%Y-%m-%d %X'),func.__name__)) 15 return wrapper 16 return deco 17 @auth('access.log') 18 def index(): 19 print('this is my index') 20 21 index()
二、迭代器、生成器、面向过程
一、 迭代器
1、迭代的概念
迭代:迭代是个重复的过程,每次重复都是基于上一次的结果来的(软件版本的迭代)
2、为何要用迭代器?
1 l=['a','b','c'] 2 n=0 3 while n < len(l): 4 print(len(n)) 5 n += 1
对于序列类型,如字符串,列表,元组,可以使用基于索引的迭代取值方式
对于没有索引的类型,如字典、集合、文件,这种方式不再适用,于是我们必须找出一种不依赖于索引的取值方式,这就是迭代器找找
3、什么是可迭代对象?什么是迭代器对象?
可迭代对象:只要对象内置有__iter__方法,obj.__iter__ 例如:字符串,列表,元组,字典,集合
1 'hello'.__iter__() 2 [1,2].__iter__() 3 (1,2).__iter__() 4 {'a':1}.__iter__() 5 {1,2,3}.__iter__()
迭代器对象:对象既有内置有__iter__方法,又内置有__next__,如文件对象
可迭代对象通过.__iter__方法,得到的结果就是迭代器对象
文件既是可迭代对象,又是迭代器对象 例如:文件
1 open('a.txt','w').__iter__() 2 open('a.txt','w').__next__()
注意:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象
4、迭代器对象的应用
next(iter_dic)这个方法和iter_dic.__next__()方法一样,推荐用next(iter_dic)这个
1 dic={'name':'alex','age':29,'sex':'male'} 2 iter_dic=dic.__iter__() 3 print(iter_dic.__next__()) 4 print(iter_dic.__next__()) 5 print(iter_dic.__next__()) ##等价于print(next(iter_dic)) 6 # print(iter_dic.__next__()) ##当没有值了,继续取值会报错
有了迭代器对象取值,所有类型的数据都可以使用(不依赖索引取值)
1 dic={'name':'alex','age':29,'sex':'male'} 2 iter_dic=dic.__iter__() 3 while True: ###可以使用try ....except....使用手工捕捉异常,避免程序崩溃 4 try: 5 k=next(iter_dic) 6 print(dic[k]) 7 except StopIteration: 8 break
使用for循环,for循环会自己处理异常
#相当于iter_dic=dic.iter__() for k in dic: print(dic[k])
for循环的工作原理
for 循环的工作原理
1、执行in后对象的dic.__iter__()方法
2、执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
3、重复过程2,知道捕捉到异常StopIteration
5、迭代器的优缺点
优点:
1、提供一种统一的迭代取值方式,该方式不再依赖于索引
2、更节省内存
缺点:
1、无法统计长度
2、一次性的,只能往后走,不能往前退,无法获取指定位置的值
应用场景:
for循环
6、判断可迭代对象和迭代器的方法
1、方法一:判断内部是不是实现了__next__方法
'__iter__' in dir(str)#如果__iter__在这个方法里面,就是可迭代的
2、方法二:
Iterable 判断是不是可迭代对象;Iterator 判断是不是迭代器
from collections import Iterable from collections import Iterator #比如给一个字符串 s='abc' print(isinstance(s,Iterable))#isinstance判断类型的 print(isinstance(s,Iterator))
判断range函数和map函数
map1=map(abs,[1,-2,3,-4]) print(isinstance(map1,Iterable)) print(isinstance(map1,Iterator))#map方法自带迭代器 s=range(100)#是一个可迭代的,但是不是迭代器 print(isinstance(s,Iterable)) print(isinstance(s,Iterator))
二、生成器
1、生成器的定义
定义:只要函数内部出现yield关键字,那么再调用该函数,将不会立即执行该函数体代码,会得到一个结果,该结果就是生成器对象
本质:生成器本质就是迭代器
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def fun(): 2 print('first') 3 yield 1,2,3 4 print('second') 5 yield 2 6 print('third') 7 yield 3 8 9 g=fun() 10 # print(next(g)) 11 for i in g: 12 print(i)
2、yield的功能
1、提供了一种自定义迭代器的方式
2、对比return,可以返回多次之,挂起函数的运行状态
自定义功能,可以生成无穷多个值,因为同一时间在内部中只有一个值(节约资源)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def my_range(start,stop,step=1): 2 while start < stop: 3 yield start 4 start += step 5 6 for i in my_range(1,1000000,2): 7 print(i)
1、send功能:
1、初始化(None),和next效果一样;2、传值
2、yield的表达式形式应用
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def eater(name): 2 food_list=[] 3 print('%s 开动啦' %name) 4 while True: 5 food = yield food_list ##将返回值保存在一个列表中 6 food_list.append(food) 7 print('%s 开始吃 %s' %(name,food)) 8 9 g=eater('alex') 10 g.send(None) ##相当于next(g) 11 g.send('骨头') ##next()功能和传值的功能 12 g.send('包子') ##可以多次传值 13 print(g.send("饺子")) ###打印返回值
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def f1(): 2 while True: 3 x=yield 4 print(x) 5 g=f1() 6 next(g) #初始化 7 g.send(12) 8 g.send(12) 9 g.send(12) 10 # g.close() ##只能传值到这个位置,在执行下面的传值,就报错 11 g.send(12) 12 g.send(12)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 def eater(name): 2 print('%s 说:我开动啦' %name) 3 food_list = [] 4 while True: 5 food = yield food_list 6 food_list.append(food) 7 print('%s eat %s' %(name,food)) 8 9 def producer(): 10 alex_g = eater('alex') 11 #第一阶段:初始化 12 next(alex_g) 13 #第二阶段: 14 while True: 15 food = input('>>:').strip() 16 if not food:continue 17 print(alex_g.send(food)) 18 producer()
3、yield from
def func(): # for i in 'AB': # yield i yield from 'AB' #AB就相当于上面的for循环,把循环简化了 # yield from [1,2,3] g = func() # print(g) #生成器 print(list(g))
4、实现:tail -f access.log | grep '404'
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # __author__ = "wzs" 4 #2017/9/28 5 with open('access.log','a') as f: 6 f.write('pythonxxx19xxxxJHHH404GG\n')
三、面向过程编程
面向过程绝对不是函数编程那么简单,对象过程是一种变成思路、思想,而变成思路是不依赖于具体语言的或语法的。
核心是过程二字,过程即解决问题的步骤,基于面向过程去设计程序就像在设计一条工业流水线,是一种机械式的思维方式
r是后面的特殊符号转换字符串
1、定义
面向过程的核心是过程,过程指的是解决问题的步骤:即先干什么再干什么。
2、优缺点
优点:复杂的问题流程化,进行简单化
缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身
3、应用
扩展性要求不高的场景,典型案例例如:Linux内核,git,httpd(程序实现的流程图)
4、范例:实现grep -rl 'root' /etc的效果,从/etc开始递归抓取文件中含有root的文件,并打印文件绝对路径,命令效果如下
[root@iZ94ao17ezcZ ~]# grep -rl 'root' /etc
/etc/passwd
/etc/rc4.d/K30postfix
/etc/rc4.d/K87restorecond
/etc/rc4.d/K85mdmonitor
/etc/rc4.d/S64mysql
/etc/rc4.d/K92ip6tables
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import os 2 def init(func): 3 def inner(*args,**kwargs): 4 g=func(*args,**kwargs) 5 next(g) 6 return g 7 return inner 8 #第一阶段:找到所有文件的绝对路径 9 def search(filepath,target): #找到一个文件路径就往下个阶段传一次 10 g = os.walk(filepath) #得到文件路径的生成器 11 for dirname, _, files in g: #拼接出想要文件的绝对路径 12 for file in files: 13 abs_file_path = r'%s\%s' % (dirname, file) 14 target.send(abs_file_path) 15 16 #第二阶段:打开文件 17 @init 18 def opener(target): 19 while True: 20 abs_file_path=yield 21 with open(abs_file_path,'rb') as f: 22 target.send((f,abs_file_path)) 23 #第三阶段:循环读出每一行内容 24 @init 25 def cat(target): 26 while True: 27 f,abs_file_path=yield 28 for line in f: 29 res=target.send((line,abs_file_path)) 30 if res: 31 break 32 #第四阶段:过滤 33 @init 34 def grep(pattern,target): 35 tag=False 36 pattern = pattern.encode('utf-8') 37 while True: 38 line,abs_file_path=yield tag 39 tag=False 40 if pattern in line: 41 target.send(abs_file_path) 42 tag=True 43 #第五阶段:打印该行属于的文件名 44 @init 45 def printer(): 46 while True: 47 abs_file_path=yield 48 print(abs_file_path) 49 search(r'G:\data\PyCharm_Project\s19\day4\a',opener(cat(grep('你好',printer()))))
三、三元表达式、列表解析、生成器表达式
一、三元表达式
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# __author__ = "wzs"
#2017/9/24
# name=input('>>:')
# if name == 'bingbing':
# print('I love you!')
# else:
# print('Goodbye!')
name=input('>>:')
#满足条件的返回结果放在最左边,不满足则放在最右边
print('I love you' if name == 'wzs' else 'Goodbye')
二、列表解析(列表推倒式)
1、范例:当产egg的数量大于3时,将超过3的部分放入仓库中
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 egg_list=[] 2 for i in range(10): 3 if i > 3: 4 res='egg %s' %i 5 egg_list.append(res) 6 7 print(egg_list)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 l=['egg%s' %i for i in range(10) if i > 3] 2 print(l)
2、语法
1 [expression for item1 in iterable1 if condition1 2 for item2 in iterable2 if condition2 3 ... 4 for itemN in iterableN if conditionN 5 ]
相当于
1 res=[] 2 for item1 in iterable1: 3 if condition1: 4 for item2 in iterable2: 5 if condition2 6 ... 7 for itemN in iterableN: 8 if conditionN: 9 res.append(expression)
3、优点:方便,改变了编程习惯,可称之为声明式编程
三、生成器表达式
1、语法
将列表推导式的[ ]换成( ),就是生成器表达式
2、范例:
g=('egg %s' %i for i in range(10) if i > 3) # print(g) #生成器 print(next(g)) #取值 print(list(g)) #生成器是迭代器对象 因而可以转成列表 输出列表中的元素
3、优点:省内存,一次在内存中只产生一个值
四、声明式编程练习题
1、将names=['egon','alex_sb','wupeiqi','yuanhao']中的名字全部变成大写
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 names=['egon','alex_sb','wupeiqi','yuanhao'] 2 names=[name.upper() for name in names] 3 print(names)
2、将names=['egon','alex_sb','wupeiqi','yuanhao']中以sb结尾的名字过滤掉,然后保存剩下的名字长度
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 names=['egon','alex_sb','wupeiqi','yuanhao'] 2 names=[len(name) for name in names if not name.endswith('sb')] 3 print(names)
3、求文件test中最长的行的长度(长度按字符个数算,需要使用max函数)
读取文件的每一行内容,然后计算出每行字符的数量,最后使用max函数取出最长一行字符的数量
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 with open('test',encoding='utf-8') as f: 2 print(max(len(line) for line in f))
4、求文件test中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数)
每次必须重新打开文件或seek到文件开头,因为迭代完一次就结束了
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 with open('test',encoding='utf-8') as f: 2 print(sum(len(line) for line in f)) #第一次计算出所有行总的字符串 3 print(sum(len(line) for line in f)) #得出的结果是0:因为第一次已将生成器的值取完,再去取,所有结果为0 4 print(sum(len(line) for line in f))
5、思考题
1 with open('a.txt') as f: 2 g=(len(line) for line in f) 3 print(sum(g)) #为何报错?
####正确的方式
1 with open('test') as f: 2 # g=(sum(len(line) for line in f)) 3 g=(len(line) for line in f) 4 print(sum(g))
6、文件shopping.txt内容如下
求总共花了多少钱?
打印出所有商品的信息,格式为[{'name':'xxx','price':333,'count':3},...]
求单价大于10000的商品信息,格式同上
a.txt文件内容如下
mouse 100.00 2 computer 4999.00 1 keyboard 300.00 1 mobile 3000.00 2 Mac 12000 1
1问:sum
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 with open('a.txt',encoding='utf-8') as f: 2 info=[line.split() for line in f] 3 cost=sum(float(unit_price)*int(count) for _,unit_price,count in info) 4 print(cost)
2问:打印出所有商品的信息
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 with open('a.txt',encoding='utf-8') as f: 2 info=[{ 3 'name':line.split()[0], 4 'price':line.split()[1], 5 'count':line.split()[2], 6 } for line in f] 7 print(info)
3问:打印单价大于10000的商品信息
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 with open('a.txt',encoding='utf-8') as f: 2 info=[{ 3 'name':line.split()[0], 4 'price':line.split()[1], 5 'count':line.split()[2], 6 } for line in f if float(line.split()[1]) > 10000] 7 print(info)