学习python第四天
一、闭包
二、装饰器
三、迭代器
四、生成器
五、函数面相过程
六、三元表达式
七、序列化
一、闭包
什么是闭包:
返回的函数对象,不仅仅是函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包过的作用域
应用领域:延迟计算(原来我们是传参,现在我们是包起来)
from urllib.request import urlopen
def index(url):
def get():
return urlopen(url).read()
return get
baidu=index('http://www.baidu.com')
print(baidu().decode('utf-8'))
二、装饰器
什么是装饰器:
装饰器他人的器具,本身同样是函数的形式,被装饰者,同样是函数的形式
装饰器的原则:
1.不修改被装饰对象的源代码
2.不修改被装饰对象的调用方式
1)无参装饰器
import time
def timel(func): #设置参数便于出入index函数
def webapper(*args,**kwargs):#设置接收所有多余的参数
start_time=time.time()
res=func(*args,**kwargs)设置res为调用函数的返回值
stop_time=time.time()
print("stop time %s" % (start_time-stop_time))
return res 返回res
return webapper 返回值设置
@timel #@timel= index=timel(index) 整下方必须是被装饰体
def index():
time.sleep(3)
print('from index')
index() 在不改变源代码和调用方式的情况下调用变量
2)有参装饰器
def auto(driver='file'): #装饰器中自带参数driver
def auto2(func):
def webapper(*args,**kwargs):
name=input('name:')
passwd=input('passwd:')
if driver == 'file':
if name == 'sunjingguo' and passwd == '123':
print('login ok')
res=func(*args,**kwargs) #这里的*,**替代了foo函数中的name,没有的话会报错,确保foo函数从inprint中调取name的值
return res
elif driver == 'ldap':
print('mysql')
return webapper
return auto2
@auto()#参数中自带默认值,可以不填写参数
def foo(name):
print(name)
foo('sunjingguo')
3)装饰器语法
被装饰函数的正上方,单独一行
@deco1
@deco2
@deco3
def foo():
pass
foo=deco1(deco2(deco3(foo)))
练习题:
编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
def auto(func):
def webapp(*args,**kwargs):
file='userinfo.txt'
name=input('user name: ')
passwd=input('passwd: ')
with open('userinfo.txt',encoding='utf-8') as read_f:
dic=eval(read_f.read()) #eval函数时间字符串转化为列表,字典,元组。前提是字符串是相同格式存储方式
if name in dic and passwd == dic[name]:
print('login ok')
res=func(*args)
return res
return webapp
@auto
def index():
print('index')
@auto
def home(name):
print('welcome %s to home' %name)
home('sunjingguo')#设定函数时,添加了形参,所以调用时也需要添加参数
阿道夫
三、迭代器
迭代器的概念:
迭代是个重复的过程,每次重复及时一次迭代,并且每次迭代的结果都是下次迭代的初始值
迭代器使用的场景:
针对序列类型:字符串,列表,元组取值时我们可以通过下标值来获取相应的值,但对字典,集合,文件这些无序的类型,就没有什么办法了,只能通过迭代器来获取相关的值了。
什么是可迭代对象:
可迭代对象是指内置有_iter_方法的对象,即“hello”._iter_
字符串,元组,列表,字典,文件都属于可迭代对象
什么是迭代对象:
可迭代对象执行‘hello’._iter_方法后得到的结果就是迭代对象
迭代对象具有内置_iter_和_next_方法的,就是迭代对象
注意:
迭代对象一定是可迭代对象,可迭代对象不一定是迭代对象
2)迭代器对象的使用
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志
#有了迭代器,我们就可以不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
try: #定义捕捉异常
k=next(iter_dic)
print(dic[k])
except StopIteration: #指定捕捉到异常后的操作
break
for 循环的原理与上述问题类似;
先执行dic._iter_()方法将可迭代对象变成迭代对象,后执行next(iter_dic)将得到的值附给循环变量,执行循环体。重复上述操作,直到try捕捉到stoplteration后为止,结束循环。
3)迭代器的优缺点:
优点:
提供了一种统一的,不依赖与索引的迭代方式
逐条赋值,节省内存空间
缺点:
无法获取长度(只有在next完毕后才知道对象有几个值)
每次循环,只能依次循环不能回退
四、生成器
什么是生成器:
只要函数内部包括yield关键字,那么执行函数的结果就是生成器,并且不会执行函数内部代码,生成器包含迭代器的特性,所以生成器就是迭代器所以生成器取值的方式与迭代器相同。
练习:
1、自定义函数模拟range(1,7,2)
def my_range(start,stop,step):
while start < stop:
yield start
start+=step
res=my_range(1,9,2)
print(next(res))
print(next(res))
print(next(res))
2、模拟管道,实现功能:tail -f access.log | grep '404'
import time
def tail(filepath): #设置函数
with open(filepath,'rb') as read_f: #以RB的方式打开文件
read_f.seek(0,2) #将鼠标移到文件最后,0表示移动多少位,2,表示从文件结尾开始(0表示从文件开头开始,1,表示从当前位置开始,2,表示从文件末尾开始)
while True:
line=read_f.readline() #line等于每行的内容,现在line的形式是字节的形式
if line: #如果line中有值
yield line #将line的值返回
else:
time.sleep(0.2)
def grep(pattern,lines):
for line in lines:
line=line.decode('utf-8')
if pattern in line:
yield line
for line in grep('404',tail('access.log')): #订制循环将上述函数传值
print(line,end='')
3)yield关键字传值
def eater(name):
print('%s 准备开始吃饭啦' %name)
food_list=[]
while True:
food=yield food_list
print('%s 吃了 %s' % (name,food))
food_list.append(food)
g=eater('egon')
g.send(None) #对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g) 第一步初始化参数,否则会有相应的错误提示
g.send('蒸羊羔')
g.send('蒸鹿茸')
g.send('蒸熊掌')
g.send('烧素鸭')
g.close() 传值结束,关闭
总结yield
1.可以将函数做成迭代器
2.对比return,可以返回多次值,可以保存函数的运行状态
五、函数面相过程
#1、首先强调:面向过程编程绝对不是用函数编程这么简单,面向过程是一种编程思路、思想,而编程思路是不依赖于具体的语言或语法的。言外之意是即使我们不依赖于函数,也可以基于面向过程的思想编写程序
#2、定义
面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么
基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式
#3、优点:复杂的问题流程化,进而简单化
#4、缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身
#5、应用:扩展性要求不高的场景,典型案例如linux内核,git,httpd
#6、举例
流水线1:
用户输入用户名、密码--->用户验证--->欢迎界面
流水线2:
用户输入sql--->sql解析--->执行功能
六、三元表达式
name=input('name')
res='NB' if name == 'sunjingguo' else 'SB' #三元表达式只针对简单的if判断形势,正确的值放在表达式的左侧,其余条件放在右侧
print(res)
egg_list=['egg%s' %i for i in range(10)]
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
类似于
res=[]
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2
...
for itemN in iterableN:
if conditionN:
res.append(expression)
#3、优点:方便,改变了编程习惯,可称之为声明式编程