python函数对象、函数嵌套、作用域、装饰器、高阶函数
一、函数对象
1、函数是第一类对象,即函数可以当作数据传递
#1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素
2、利用该特性,优雅的取代多分支的if
def test(): print('test') def tes1(): print('test1') d={'test':test,'test1':tes1} while True: s=input('-->').strip() if s in d: d[s]()
二、函数嵌套
1、函数的嵌套调用
def test(x,y): if x>y: return x else: return y def test1(a,b,c,d): r1=test(a,b) r2=test(r1,c) r3=test(r2,d) return r3 print(test1(1,2,3,4)) 运行结果: 4
2、函数的嵌套定义
def f1(): def f2(): def f3(): print('from f3') f3() f2() f1() f3() #报错,为何?请看下一小节
三、重点 作用域
1、Python中的作用域分4种情况 :
L:local,局部作用域,即函数中定义的变量
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的
G:global,全局变量,就是模块级别定义的变量
B: built - in,系统固定模块里面的变量,比如int,bytearray等
x = int(2.9) # int built-in g_count = 0 # global def outer(): o_count = 1 # enclosing i_count = 8 def inner(): # i_count=2 #local print(i_count) inner() outer()
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下 x=1 def f1(): def f2(): print(x) return f2 x=100 def f3(func): x=2 func() x=10000 f3(f1())
3、global与nonlocal关键字
#1.global关键字 count=10 def outer(): global count count=5 print(count) outer() #nonlocal关键字 如果要在一个嵌套的函数中修改嵌套作用域中的变量,则须使用nonlocal关键字。 def test(): a=1 def test1(): nonlocal a a=2 print('test1:',a) test1() print('test:',a) test()
小结:
1、变量查找顺序:legb,作用域局部>外层作用域>当前模块中的全局>python内置作用域
2、只有模块、类、及函数才能引入新作用域
3、对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会
使用外部作用域的变量
4、内部作用域修改外部作用域时,全局变量要使用global关键字,嵌套作用域
变量要使用nonlocal关键字。nonlocal是Python3新增的关键字,有了此关键字,就能
完美的实现闭包了
四、闭包函数
1、什么是闭包
内部函数包含对外部作用域而非全局作用域的引用 提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来
def counter(): n=0 def incr(): nonlocal n x=n n+=1 return x return incr c=counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) #查看闭包的元素
2、闭包的意义:
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 #应用领域:延迟计算(原来我们是传参,现在我们是包起来) 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、不修改被修饰函数的调用方式
装饰器的只是储备
装饰器=高阶函数+函数嵌套+闭包
无参数装饰器 import time def timmer(func): def wapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('函数运行时间是%s' % (stop_time - start_time)) return res return wapper @timmer def cal(l): res = 0 for i in l: res += i return res print(cal(range(100))) 有参数装饰器 def t1(d='file'): def t2(func): def test(*args,**kwargs): name=input('name:') pwd=input('pwd:') if d == 'file': if name=='root' and pwd=='123': print('成功') res=func(*args,**kwargs) return res elif d=='lala': print('lala') return test return t2 @t1(d='file') def test3(name): print(name) test3('root')
3、装饰器补充:装饰器补充:wraps
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco #相当于index=deco(index) def index(): '''哈哈哈哈''' print('from index') print(index.__doc__)
4、叠加多个装饰器
1. 加载顺序(outter函数的调用顺序):自下而上 2. 执行顺序(wrapper函数的执行顺序):自上而下
def outter1(func1): #func1=wrapper2的内存地址 print('加载了outter1') def wrapper1(*args,**kwargs): print('执行了wrapper1') res1=func1(*args,**kwargs) return res1 return wrapper1 def outter2(func2): #func2=wrapper3的内存地址 print('加载了outter2') def wrapper2(*args,**kwargs): print('执行了wrapper2') res2=func2(*args,**kwargs) return res2 return wrapper2 def outter3(func3): # func3=最原始的那个index的内存地址 print('加载了outter3') def wrapper3(*args,**kwargs): print('执行了wrapper3') res3=func3(*args,**kwargs) return res3 return wrapper3 @outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址 @outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址 @outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址 def index(): print('from index') print('======================================================') index()
5、验证功能装饰器,模拟session
# 函数闭包模拟session,带参数装饰器 # 1、手写用户信息 user_list = [{'name': 'lala', 'passwd': '123'}, {'name': 'wawa', 'passwd': '123'}] #2、从文件中读取用户信息,再生成列表 # file='22.txt' # with open(file,'r',encoding='utf-8') as f: # user_list=[] # for line in f.readlines(): # li=line.strip('\n') # # li=line.split(',') # user_list.append(li) #读出文件信息后,重新生成用户信息,试过用eval,但是报错 # print(user_list) #最开始没有用户,登录状态是FALSE current_dic={'username':None,'login':False} #加认证参数,不同的函数使用不同的认证类型 def au_rz(rz_type='111'): def rzfunc(func): def test(*args, **kwargs): #最开始判断没有用户的时候,直接就执行函数 global current_dic print('各自的认证类型是:',rz_type) if rz_type=='111': if current_dic['username'] and current_dic['login']: res=func(*args,**kwargs) return res #输入用户名密码 uname=input('用户名:').strip() pwd=input('密码:').strip() #再遍历current_dic的用户状态是否与user_list中一致 for current_dic in user_list: if uname==current_dic['name'] and pwd==current_dic['passwd']: current_dic['username']=uname current_dic['login']=True res=func(*args,**kwargs) return res else: print('错误啦啦啦') elif rz_type=='222': res=func(*args,**kwargs) return res else: print('认证错误') return test return rzfunc @au_rz(rz_type='111') def index(name): print('%s欢迎进入主页' % name) @au_rz(rz_type='22') def shopping_cart(name,commodity): print('%s的购物车里有%s' % (name, commodity)) print('未登录前状态--》',current_dic) index('主人') print('登录后状态---》',current_dic) shopping_cart('主人','衣服')
六、高阶函数
1、高阶函数定义:
- 1、函数接收的参数是一个函数名
- 2、函数的返回值是一个函数名
- 3、满足上述条件任意一个,都可以成为高阶函数
#1、高阶函数接收的参数是一个函数名 import time def f1(): time.sleep(1) print('lalala') def test(func): print(func) start_time = time.time() func() stop_time = time.time() print('函数运行时间是%s' % (stop_time - start_time)) test(f1) #2、函数的返回值是一个函数名 def fo1(): print('来自foo1') def test(func): start_time = time.time() return func stop_time = time.time() print('函数运行时间是%s' % (stop_time - start_time)) fo1 = test(fo1) fo1()
2、装饰器的架子,装饰器=高阶函数+函数嵌套+闭包
View Codeimport time def timmer(func1): # func1==test def wrapper(): # print(func1) start_time = time.time() func1() # 就是在运行test() stop_time = time.time() print('运行时间是%s' % (stop_time - start_time)) return wrapper def test(): time.sleep(1) print('test函数运行完毕') test = timmer(test) # 返回的是wrapper地址 test() # 执行的是wrapper @timmer #就相当于test=timmer(test) # 函数闭包加上返回值、参数 def timmer1(func2): def wapper(*args, **kwargs): start_time = time.time() res = func2(*args, **kwargs) # 就是在运行test() stop_time = time.time() print('test1函数运行时间是%s' % (stop_time - start_time)) return res return wapper @timmer1 # test1=timmer(test1) def test1(name, age, gender): time.sleep(1) print('test1函数运行完毕,name:[ %s],age:[%s],性别 [%s]' % (name, age, gender)) return '这是test返回值' # test1=timmer1(test1) # test1() res = test1('zhuzhu', 20, '女') print(res) ## 函数闭包补充解压序列 # a,b,c=(1,2,3)#若是多一个参数或多一个值,报错,要一一对应
练习:
六 练习题
一:编写函数,(函数执行的时间是随机的)
二:编写装饰器,为函数加上统计时间的功能
三:编写装饰器,为函数加上认证的功能四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
七:为题目五编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
注意:时间格式的获取
import time
time.strftime('%Y-%m-%d %X')
View Code# 一:编写函数,(函数执行的时间是随机的) import time, random,requests,os import time def random_time(): #将时间设置成元组 a=(1997,4,12,0,0,0,0,0,0) b=(2020,4,12,0,0,0,0,0,0) #生成时间戳 start=time.mktime(a) end=time.mktime(b) t=random.randint(start,end) date=time.localtime(t) #时间戳生成元组 date_str=time.strftime('%Y-%m-%d %X',date) #设置格式 print('打印随机时间:',date_str) if __name__ =='__main__': random_time() #2、3 import time def rz(rz_type='abc'): def timmer(func): def test(*args,**kwargs): print('认证类型是:',rz_type) if rz_type=='abc': star=time.time() res=func(*args,**kwargs) stop=time.time() print('函数运行时间%s'%(stop-star)) return res else: print('认证失败') return test return timmer # @timmer #第2题 @rz(rz_type='abc') #第三题,abc认证成功 # @rz(rz_type='ab') #第三题,失败 def cou(a,b): time.sleep(1) print('加法计算结果:%s'%(a+b)) if __name__=='__main__': cou(1,2) #4、 # 1、手写用户信息 user_list = [{'name': 'lala', 'passwd': '123'}, {'name': 'wawa', 'passwd': '123'}] #2、从文件中读取用户信息,再生成列表 # file='22.txt' # with open(file,'r',encoding='utf-8') as f: # user_list=[] # for line in f.readlines(): # li=line.strip('\n') # # li=line.split(',') # user_list.append(li) #读出文件信息后,重新生成用户信息,试过用eval,但是报错 # print(user_list) #最开始没有用户,登录状态是FALSE current_dic={'username':None,'login':False} #加认证参数,不同的函数使用不同的认证类型 def au_rz(rz_type='111'): def rzfunc(func): def test(*args, **kwargs): #最开始判断没有用户的时候,直接就执行函数 global current_dic print('各自的认证类型是:',rz_type) if rz_type=='111': if current_dic['username'] and current_dic['login']: res=func(*args,**kwargs) return res #输入用户名密码 uname=input('用户名:').strip() pwd=input('密码:').strip() #再遍历current_dic的用户状态是否与user_list中一致 for current_dic in user_list: if uname==current_dic['name'] and pwd==current_dic['passwd']: current_dic['username']=uname current_dic['login']=True res=func(*args,**kwargs) return res else: print('错误啦啦啦') elif rz_type=='222': res=func(*args,**kwargs) return res else: print('认证错误') return test return rzfunc @au_rz(rz_type='111') def index(name): print('%s欢迎进入主页' % name) @au_rz(rz_type='22') def shopping_cart(name,commodity): print('%s的购物车里有%s' % (name, commodity)) print('未登录前状态--》',current_dic) index('主人') print('登录后状态---》',current_dic) shopping_cart('主人','衣服') # 五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录 user = {'user': None, 'login_time': None, 'timeout': 0.000001} import random,time def rz(func): def test(*args, **kwargs): if user['user']: timeout = time.time() - user['login_time'] if timeout < user['timeout']: return func(*args, **kwargs) name = input('用户名:').strip() pwd = input('密码:').strip() if name == 'ww' and pwd == '123': user['user'] = name user['login_time'] = time.time() res = func(*args, **kwargs) return res return test @rz def index(): time.sleep(random.randrange(3)) print('welcom to index') @rz def home(name): time.sleep(random.randrange(3)) print('%s、%s 主人welcom to home' % (name, name1)) index() home('男') # 六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 import requests def page(url) return requests.get(url).text a=page('https://www.baidu.com/') print(a) #七:为题目五编写装饰器,实现缓存网页内容的功能: #具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中 #扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中 import requests,os file='2.txt' def frw(func): def test(*args,**kwargs): if not os.path.exists(file): with open(file,'w'):pass if os.path.getsize(file): with open(file,'r',encoding='utf-8') as f: res=f.read() else: res=func(*args,**kwargs) with open(file,'w',encoding='utf-8') as f: f.write(res) return res return test @frw def get_url(url): return requests.get(url).text res=get_url('https://www.baidu.com/') print(res) #7,扩展功能 import requests,os,hashlib engine_settings={ 'file':{'dirname':'./db'}, 'mysql':{ 'host':'127.0.0.1', 'port':3306, 'user':'root', 'password':'123'}, 'redis':{ 'host':'127.0.0.1', 'port':6379, 'user':'root', 'password':'123'}, } def make_cache(engine='file'): if engine not in engine_settings: raise TypeError('egine not valid') def deco(func): def wrapper(url): if engine == 'file': m=hashlib.md5(url.encode('utf-8')) cache_filename=m.hexdigest() cache_filepath=r'%s/%s' %(engine_settings['file']['dirname'],cache_filename) if os.path.exists(cache_filepath) and os.path.getsize(cache_filepath): return open(cache_filepath,encoding='utf-8').read() res=func(url) with open(cache_filepath,'w',encoding='utf-8') as f: f.write(res) return res elif engine == 'mysql': pass elif engine == 'redis': pass else: pass return wrapper return deco @make_cache(engine='file') def get(url): return requests.get(url).text # print(get('https://www.python.org')) print(get('https://www.baidu.com')) #8、八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作 d={} def make_dic(name): def test1(func): d[name]=func return test1 @make_dic('t1') def t1(): print('t1') @make_dic('t2') def t2(): print('t2') @make_dic('t3') def t3(): print('t3') @make_dic('t4') def t4(): print('t4') print(d) ''' 九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定 注意:时间格式的获取 import time time.strftime('%Y-%m-%d %X') ''' import time def frw(logfiel): def test(func): if not os.path.exists(logfiel): with open(logfiel,'w'):pass def write(*args,**kwargs): res=func(*args,**kwargs) t=time.strftime('%Y-%m-%d %X',time.localtime()) with open(logfiel,'a',encoding='utf-8') as f: f.write('%s %s run\n'%(t,func.__name__)) return res return write return test @frw(logfiel='22') def f1(): print('f1 run') f1()
第4题eval写法#第4题,eval写法,但是老是报错,麻了 db='22.txt' login_status={'user':None,'status':False} def auth(auth_type='file'): def auth2(func): def wrapper(*args,**kwargs): if login_status['user'] and login_status['status']: return func(*args,**kwargs) if auth_type == 'file': with open(db,'r',encoding='utf-8') as f: dic=eval(f.read()) name=input('username: ').strip() password=input('password: ').strip() if name in dic and password == dic[name]: login_status['user']=name login_status['status']=True res=func(*args,**kwargs) return res else: print('username or password error') elif auth_type == 'sql': pass else: pass return wrapper return auth2 @auth() def index(): print('index') @auth(auth_type='file') def home(name): print('welcome %s to home' %name) index() home('lala')

浙公网安备 33010602011771号