python中的闭包函数,装饰器,迭代器,生成器,以及匿lambda和递归函数
一 闭包函数:
嵌套函数,内部函数调用外部函数的变量,需要强调的是:
若在函数中返回闭包函数的地址,则闭包函数内使用的外部的变量不会在内存 销毁
# def outer():
# a=1
# def inner():
# print(a)
# print(inner.__closure__)# 只要返回的内存地址以cell开头就证明是闭包
# outer()
'''
若在函数中返回闭包函数的地址,
则闭包函数内使用的外部的变量不会在内存销毁
'''
# def outer():
# a=1
# def inner():
# print(a)
# return inner
# inn=outer()
# inn()
from urllib.request import urlopen
def get_url():
urla='http://www.jd.com/'
def gets():
rest=urlopen(urla).read()
print(rest)
return gets
ss=get_url()
ss()
二 装饰器
本质就是一个闭包函数。
作用:
不想修改函数的调用方式,但是还想在原来的函数前后添加功能
原则:
开放封闭原则。
开放对扩展是开放的(对已经完成的功能进行扩展)
封闭对修改是封闭的(已经完成的功能不能在修改)
def timer(f):
# #闭包函数 如果返回闭包函数的内存地址 则闭包函数引用的外部变量不回被销毁
# def inner(a):
# start=time.time()
# print('f',f)
# ret=f(a)
# end=time.time()
# print(end-start)
# return ret
# print('inner',inner)
# return inner
#
#
# @timer
# def func(a):
# time.sleep(0.1)
# print('aaa')
# print(a)
# return '新年好'
# #>>>>>>在加语法糖之后,
# # 实际上被装饰函数func已经作为参数传给了装饰函数,装饰函数返回
# #inner的内存地址 所以这时候的func是inner的内存地址加括号调用后
# #得到inner函数的返回值
# result=func(1)
# print(result)
装饰器最终形态:
def times(f):#times是装饰器函数名, f是被装饰的函数
# def inner(*args,**kwargs):
# start=time.time()
# res=f(*args,**kwargs)
# end=time.time()
# print(end-start)
# print('装饰函数')
# return res
# return inner
#
#
# @times
# def func_a(*args,**kwargs):
# print('rucan',args,kwargs)
# return args,kwargs
#
# result=func_a(['sdfsdf',444],c={'NAME':555})
# print('result',result)
functools
# from functools import wraps
# def login_check(f):
# # functools中提供的装饰器 如果不加则实际在调用被装饰函数的时候
# #调用的是装饰器内的闭包函数,这一点可以只用.__name__方法验证
# #但是如果加了 就是不会影响原函数
# @wraps(f)
# def inner(*args,**kwargs):
# res=f(*args,**kwargs)
# username=res[1]['username']
# pwd=res[1]['pwd']
# print('pwd',pwd)
# with open('loginword',encoding='utf-8')as f1:
# for i in f1:
# print(i)
# if username in i:
# if i.split(" ")[1].strip()==pwd:
# print('login pass')
# return True
# else:
# print('请输入正确密码')
# return False
# return inner
#
#
# @login_check
# def login(*args,**kwargs):
# print(args,kwargs)
# return args,kwargs
#
# login(username='yuanbao',pwd='hanqing')
# print(login.__name__)
带参数的装饰器:
本质是 装饰器又封装了一层外部函数,定义一个标志位flage穿进去,装饰器对多三层,并没有改变装饰器的原来的调用方式。
import time
# FlAGE=1
# def timer_out(flag):
# def timer(func):
# def inner(*args,**kwargs):
# if flag:
# start=time.time()
# ret=func(*args,**kwargs)
# end=time.time()
# print(end-start)
# return ret
# else:
# ret=func(*args,**kwargs)
# print('没有执行装饰器')
# return ret
# return inner
# return timer
#以下两句基本相等
# timer=timer_out(FlAGE)
# @timer
# @timer_out(FlAGE)
# def wahah():
# time.sleep(0.1)
# print('wahah')
多个装饰器装饰一个函数:
如果有多个装饰器,装饰顺序是从里到外(就近原则),但是执行顺序是从外到里
# def warper1(f):
# def inner1():
# print('start warper1')
# res=f()
# print('end inner1')
# return res
# return inner1
#
# def warper2(f):
# def inner2():
# print('start warper2')
# res=f()
# print('end inner2')
# return res
# return inner2
#
# def warper3(f):
# def inner3():
# print('start warper3')
# res=f()
# print('end inner3')
# return res
# return inner3
# @warper3 #f=warper(f)>>>>waper3(inner2)==inner3
# @warper2 #f=warper(f)>>>>waper2(inner1)==inner2
# @warper1 #f=warper1(f)=inner1
# def func_01():
# print("func_01")
# return '多层装饰器'
#
# func_01()
例子:有两个函数一个用来记录用户登录状况一个用来记录程序执行的时间
import time
def login_time(f):
def inner(*args,**kwargs):
start=time.time()
time.sleep(0.1)
res=f(*args,**kwargs)
end=time.time()
print(end-start)
return res
return inner
def login_status(f):
def inner(*args,**kwargs):
print('判断登录状态')
print(args,kwargs)
result=f(*args,**kwargs)
username=result[1]['name']
pwd=result[1]['pwd']
with open('loginword',encoding='utf-8')as f1:
for i in f1:
if username in i:
listuser=i.strip().split()
if username==listuser[0] and pwd==listuser[1]:
print('login pass')
return True
else:
print('请检查密码')
return False
return inner
@login_time
@login_status
def login(*args,**kwargs):
print("login",args,kwargs)
return args,kwargs
result=login(name='yuan',pwd='nihao')
print("result",result)
三 迭代器
迭代: 迭代是一个重复的过程,并且每次的重复都是基于上一次的结果而来。
迭代器:迭代取值的工具,不依靠索引取值,可迭代的对象执行.__iter__方法得到的返回值就是迭代器对象,提供了一种不依赖索引取值的方式。同样的,迭代器取值麻烦,只能一个一个的取,只能往后取,并且是一次性的,无法使用len方法计算长度
可迭代对象即可以简单的理解成能被for循环的类型:
str,list,dict,set,tupe,f=open(),range(),enumerate
其中文件类型本身就是迭代器对象,因为可以直接调用.__next__方法
py2 py3的range区别 py2直接比那里后生成列表,py3中直接返回range对象.__iter__一个一个的取值,节省空间
可迭代对象加.__iter__就是一个迭代器,只要含有.__iter__方法的都是可迭代的---可迭代协议
通过next就可以从迭代器中一个一个的取值
查询数据类型所拥有的所有方法 dir
# print(dir({}))
# print(dir([]))
# print(dir(''))
# print(dir(range(10)))
# rest=set(dir([]))&set(dir([]))&set(dir(''))&set(dir(range(10)))
# print(rest)
# print(dir(int))
缺点:迭代器取值麻烦,只能一个一个的取,只能往后取,并且是一次性的,无法使用len方法计算长度
lista=[1,2,3,'abc']
iterators=lista.__iter__()
iterators_01=iter(lista)
print(iterators_01,"\n",iterators)
print(iterators is iterators_01)
# while True:
# try:
# print(iterators.__next__())
# except StopIteration:
# break
# print("第二次取值》》》》》》》")
# iterators=lista.__iter__()
# while True:
# try:
# print(iterators.__next__())
# except StopIteration:
# break
# # print(dir(lista))
# print(dir(iterators))
# print(set(dir(iterators))-set(dir(lista)))
四 生成器
生成器与迭代器:
1 生成器:造出迭代器
2 生成器:本质就是迭代器,也就是说生成器的玩法其实就是迭代器的玩法
函数内有yeild 都回被认为是生成器,打印该函数的执行结果返回生成器 内存地址,并不会执行函数内代码
3 生成器是惰性运算
yeild总结;
1 提供了一种自定义迭代器的方式,可以在函数内使用yeild关键字,调用 函数拿到的结果就是一个生成器,生成器就是迭代器
2 yeild可以像return一样用于返回值,区别是return只能返回一次值,但是 yeild可以返回多次,因为yeild可以保存函数执行的状态
# def chicken():
# print('one')
# yield 1
# print("two")
# yield 2
# print('three')
# yield 3
# print(chicken())
# s=chicken().__iter__()
# s1=s.__next__()
# print(s1)
# 1 ss=chicken().__iter__() 拿到迭代器
# 2 触发 ss.__next__(),拿到该方法内的yield返回的值,赋值给a
# 3 周而复始,知道函数内不在有yield 取值完毕
# 4 for循环会检测到StopIteration 异常 结束循环
# ss=chicken()
# for a in ss:
# print(a)
# print(s.__next__()
for循环底层实现原理:
# def my_gen(start,end,steps=1): # n=start # while n<end: # yield n # n += steps # # s10=my_gen(0,10,2) # print(s10.__next__()) # print(s10.__next__()) # print(s10.__next__()) # # for i in range(0,10,2): # print(i)
yeild表达式
def eat(name):
print("%s ready to eat"%name)
while True:
print("1")
food=yield
print("%s start to eat %s"%(name,food))
dog1=eat("alex")
print(dog1)
#表达式 必须要先初始化 让函数停在yeild的位置
dog1.__next__()
#2 接下来的事情就是喂狗
#send有两方面的功能
#1 给yeild传值
# #2 同__next__一样 再次迭代
dog1.send("骨头")
# dog1.send("鱼")
send 获取下一个值的效果和next基本一致,只是在获取下一个值的时候,给上一个yeild的位置传递一个数据,使用send的注意事项
第一次使用生成器的时候,使用next获取下一个值
最后一个yield不接受外部的值
生成器进阶 获取移动平均数:
# def inits(f): # def inner(*args,**kwargs): # a=f(*args,**kwargs) # a.__next__() # return a # return inner # # # @inits # def average(): # sum=0 # count=0 # avg=0 # while True: # num=yield avg # sum+=num # count+=1 # avg=sum/count # # avg_g=average() # ret=avg_g.send(10) # print(ret) # ret=avg_g.send(30) # print(ret)
yield from使用 用于从对象中取值
普通写法
def genertor():
a='qwertyuio'
b='asdfghjk'
for i in a:
yield i
for j in b:
yield j
g=genertor()
g1=list(g)
print(g1)
# for i in g:
# print(i)
#添加from的写法
def genertor_01():
a = 'qwertyuio'
b = 'asdfghjk'
yield from a
yield from b
ggg=genertor_01()
for i in ggg:
print(i)
五 匿名函数lambda
格式:
函数名=lambda 入参:返回值
参数可以有多个,但需要用逗号隔开。匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束之后内容就是返回值,返回值和正常函数一样可以是任意的数据类型
# calc=lambda n:n*n # print(calc(10)) # # add01=lambda a,b:a+b # print(add01(1,2))
lambda函数通常和带有key关键字参数的内置函数配合使用,min max map filter sorted 例如sorted(lista,key=len) 一定是先遍历列表lista 在调用len函数计算
#字典排序 max先回判断入参是什么类型 在根据类型采用对应的取值方法判断
dic={'k1':10,'k2':100,'k3':30}
print(max(dic,key=lambda k:dic[k]))
lista=[4,2,3,1]
print(max(lista,key=lambda x:lista[x-1]))
listb=[3,2,100,888,999,213,1111]
res=map(lambda x:x**2,[1,2,6,8])
print(list(res))
筛选列表中大于10的数字
# res1=filter(lambda n:n>10,listb) # print(list(res1))
现有两个元组((('a'),('b')),(('c'),('d')))请使用python中的匿名函数变成[{'a':'c','b':'d'}]
# tup_01=zip((('a'),('b')),(('c'),('d')))
# print(tup_01)
#
# def func_01(tup):
# return {tup[0]:tup[1]}
# #ret=map(func_01,tup_01)
# ret01=map(lambda tup:{tup[0]:tup[1]},tup_01)
# #print(list(ret))
# print(list(ret01))
# # for i in tup_01:
# print(i)
列表生成式和生成器生成式的应用
def multipliers(x):
return [lambda x:i*x for i in range(4)]
ret=multipliers(2)
print((ret))
#
for i in ret:
r=i(4)
print(r)
结果:
12
12
12
12
很明显 出这道题的面试官 想要的结果是0 4 8 12
但是为什么是4个12呢?
这是因为multipliers返回的是列表表达式,里面的lambda表达式已经生成了,本质上lambda也是函数需要加括号才能执行,ret中的i实际实际上已经等于3了
如果想要得到想要的结果只需要变列表表达式为生成器表达式
def multipliers():
return (lambda x:i*x for i in range(4))
ret=multipliers()
print(ret)
print([m(4) for m in multipliers()])
这样就可以了
map和filter内置函数结合lambda函数
# # 1 用map函数来处理字符串列表 把列表中所有人都变成ss例如alex_ss # name=['alex','wupeiq','wang','yong','mei'] # print(list(map(lambda x:x+'_ss',name))) # # #2 用filter函数处理数字列表,将列表中所有的偶数筛选出来 # num=[1,2,3,5,6,8,10] # print(list(filter(lambda x:x%2==0,num)))
写一个 20行以上的文件,运行程序,将内容读取的内容,用列表储存
模拟用户输入页码,每页5条,仅输出当前也的内容
# def file_read(filename):
# with open(filename,encoding='utf-8')as f:
# ret=f.readlines()
# return ret
#
#
# def filter_num(ret_num):
# if ret_num>0:
# ret_num=ret_num*5-5
# ret_con=file_read('examtest')
# #ret=[x for x in ret_con lambda x:x[ret_num:ret_num+5]]
# ret=filter(lambda x:ret_con[ret_num::],ret_con)
# return ret
#
# ret=filter_num(2)
# print(list(ret))
with open('examtest',encoding='utf-8')as f1:
l=f1.readlines()
page_num=input('页码')
page_num=int(page_num)
pages,mod=divmod(len(l),5)
if mod:
pages+=1
if page_num>pages or page_num<=0:
print('输入有误')
elif page_num==pages and mod!=0:
for i in range(mod):
print(l[(page_num-1)*5+i].strip())
else:
for i in range(5):
print(l[(page_num-1)*5+i].strip())
每个小字典name对应股票名字,shares对应多少股,price对应价格
# portfolio=[{'name':'IBM','shares':100,'price':91.1},
# {'name':'AAPL','shares':50,'price':543.22},
# {'name':'FB','shares':200,'price':21.09},
# {'name':'HPQ','shares':35,'price':31.75},
# {'name':'YHOO','shares':45,'price':16.35},
# {'name':'ACME','shares':75,'price':115.65}]
# # 4.1 计算购买每只股票的的总价
# ret=map(lambda dic:{dic['name']:dic['shares']*dic['price']},portfolio)
# print(list(ret))
#
# #4.2 用filter过滤出 单价大于100的股票有那些
# ret0=filter(lambda dict:dict['price']>100,portfolio)
# print(list(ret0))
六 递归函数
在函数中调用自身函数 默认递归深度998 。可以使用sys模块中的setrecursionlimit修改递归深度。
如果递归次数太多,就不适合使用递归来解决。超过最大递归深度报错,所有使用递归函数,必须要有结束条件
优点: 会让代码简单
缺点:占内存
递:往下走 问题分析一次一次往下走
归:往上走 结果一次一次往上走
求年龄
# def age_01(n): # if n==4: # return 40 # elif n<4 and n>0: # return age_01(n+1)+2 # # # ages=age_01(1) # print(age_01(1))
二分查找算法 先排序 在从中间取值对比目标
# lista=[2,3,4,5,6,55,66,77,88,99,100]
# def finds(l,aim):
# mid_index=len(l)//2
# if l[mid_index]<aim:
# new_l=l[mid_index+1:]
# finds(new_l,aim)
# elif l[mid_index]>aim:
# new_l=l[0:mid_index]
# finds(new_l,aim)
# else:
# print('pass',mid_index,l[mid_index])
#
# finds(lista,66)
# listb=[2,3,4,5,6,55,66,77,88,99,100]
# def finds_01(l,aim,start=0,end=None):
# #如果这里end写成len(l)则下边递归函数再次调用的时候
# #等于给end赋值还是原长度,那么下边递归函数传值的end
# #就没有用处 在第一次调用的时候 已经肯定了end的值就是
# #列表的长度
# end=len(l) if end is None else end
# print('end',end,'start',start)
# mid_index=(end-start)//2+start
# if start<=end:
# if l[mid_index]<aim:
# res1=finds_01(l,aim,start=mid_index+1,end=end)
# # 需要将结果return 返回给上层函数
# return res1
# elif l[mid_index]>aim:
# res2=finds_01(l,aim,start=start,end=mid_index)
# return res2
# else:
# print('找到了',mid_index,l[mid_index])
# return mid_index
# else:
# print('找不到这个值')
# return '找不到这个值1q'
#
#
#
# s=finds_01(listb,66)
# print(s)
实现阶乘
def facrotial(x):
if x<2:
return 1
elif x>=2:
return facrotial(x-1)*x
facrotial(4)
推导过程
'''
def facrotial(4):
if x<2:
return 1
elif x>=2:
return facrotial(4-1)*4
def facrotial(3):
if x<2:
return 1
elif x>=2:
return facrotial(3-1)*3
def facrotial(2):
if x<2:
return 1
elif x>=2:
return facrotial(2-1)*2
def facrotial(1):
if x<2:
return 1
elif x>=2:
return facrotial(x-1)*x
'''
递归函数的返回值问题:
1 递归深度
2 不要只看到return就认为已经返回了,要看返回操作是在递归到第几层发生的,然后
返回给谁了,如果不是分会给最外层函数,调用者就接受不到,需要在分析如何把结果返回回来
斐波那契数列
def fab_num(x):
if x<=2:
return 1
elif x>2:
return fab_num(x-2)+fab_num(x-1)
print(fab_num(4))
以上斐波那契数列使用了双递归,慎用。4c,16g内存,求第1000个的时候,楼主去厕所,回来电脑死机了。

浙公网安备 33010602011771号