Day4 闭包、装饰器decorator、迭代器与生成器、面向过程编程、三元表达式、列表解析与生成器表达式、序列化与反序列化

一、装饰器 一、装饰器的知识储备   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 3 1、定义在函数内部的函数 2、包含对外部作用域名字的引用,而不是对全局作用域名字的引用 那么该内部函数称之为闭包函数   2、实例 View Code   3、应用:延迟计算/惰性计算(爬网页) 方式一 优化 复制代码 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() 复制代码 1 2 3 4 5 6 7 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、装饰器的定义和原则 1 2 3 4 5 装饰器本身可以是任意可以调用对象,被装饰的对象本身也可以是任意可调用对象 定义:本质是函数,(装饰其他函数),就是为其他函数添加附加功能    在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。 原则:1、不能修改被装饰的函数的源代码 2、不能修改被装饰的函数的调用方式   添加统计执行时间的功能 修改源代码 不修改源代码,修改调用方式   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() 复制代码 被装饰对象有参数,参数类型和数量不固定 有认证功能的装饰 显示被装饰对象的注释信息 有参数的装饰器 db.txt   装饰器最多三层函数,三层几乎满足所有的需求了   4、练习题   一:编写函数,(函数执行的时间是随机的) View Code   二:编写装饰器,为函数加上统计时间的功能 View Code   三:编写装饰器,为函数加上认证的功能 View Code   四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码 注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式 View Code   五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录 View Code   六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 View Code   七:为题目五编写装饰器,实现缓存网页内容的功能: 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中 扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中 View Code   八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作 View Code   九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定 注意:时间格式的获取 import time time.strftime('%Y-%m-%d %X') View Code 二、迭代器、生成器、面向过程 一、 迭代器   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、迭代器对象的应用 复制代码 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循环的工作原理 1 2 3 4 for 循环的工作原理 1、执行in后对象的dic.__iter__()方法 2、执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码 3、重复过程2,知道捕捉到异常StopIteration   5、迭代器的优缺点 优点: 1、提供一种统一的迭代取值方式,该方式不再依赖于索引 2、更节省内存 缺点: 1、无法统计长度 2、一次性的,只能往后走,不能往前退,无法获取指定位置的值   应用场景:   for循环 二、生成器   1、生成器的定义 1 2 定义:只要函数内部出现yield关键字,那么再调用该函数,将不会立即执行该函数体代码,会得到一个结果,该结果就是生成器对象 本质:生成器本质就是迭代器 范例:yield   2、yield的功能 1 2 1、提供了一种自定义迭代器的方式 2、对比return,可以返回多次之,挂起函数的运行状态   自定义功能,可以生成无穷多个值,因为同一时间在内部中只有一个值(节约资源) 使用迭代器,实现range功能   send功能:1、初始化(None),和next效果一样;2、传值   yield的表达式形式应用 send应用 无限传值 多个函数来回切换(传值),下次传值在上次暂停的地点继续 实现:tail -f access.log | grep '404' tail -f access.log | grep '404' 向access.log追加内容 三、面向过程编程 面向过程绝对不是函数编程那么简单,对象过程是一种变成思路、思想,而变成思路是不依赖于具体语言的或语法的。 核心是过程二字,过程即解决问题的步骤,基于面向过程去设计程序就像在设计一条工业流水线,是一种机械式的思维方式 r是后面的特殊符号转换字符串   1、定义 面向过程的核心是过程,过程指的是解决问题的步骤:即先干什么再干什么。   2、优缺点 优点:复杂的问题流程化,进行简单化 缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身   3、应用 扩展性要求不高的场景,典型案例例如:Linux内核,git,httpd(程序实现的流程图)   4、范例:实现grep -rl 'root' /etc的效果,从/etc开始递归抓取文件中含有root的文件,并打印文件绝对路径,命令效果如下 1 2 3 4 5 6 7 [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 实现方法 三、三元表达式、列表解析、生成器表达式 一、三元表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/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的部分放入仓库中 常规写法 列表推倒式   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']中的名字全部变成大写 列表推导式   2、将names=['egon','alex_sb','wupeiqi','yuanhao']中以sb结尾的名字过滤掉,然后保存剩下的名字长度 列表推导式   3、求文件test中最长的行的长度(长度按字符个数算,需要使用max函数)   读取文件的每一行内容,然后计算出每行字符的数量,最后使用max函数取出最长一行字符的数量 生成器表达式   4、求文件test中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数)   每次必须重新打开文件或seek到文件开头,因为迭代完一次就结束了 生成器表达式   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内容如下 1 2 3 求总共花了多少钱? 打印出所有商品的信息,格式为[{'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 1问 总花费   2问:打印出所有商品的信息 列表推导式   3问:打印单价大于10000的商品信息 列表推导式http://www.cnblogs.com/happy-king/p/7589328.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信