目录
一、闭包 - 内部函数包含,对外层作用域而非全局作用域的引用
2-5 @wraps 对装饰器内层函数的使用 -- 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性
一、闭包 - 内部函数包含,对外层作用域而非全局作用域的引用
def counter(): n = 0 def incr(): nonlocal n # 修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量 x = n n += 1 return x return incr c = counter() print(c()) # 第一次 n=0 x=0 n++ print(c())# 2 n=1 x=1 n++ print(c()) print(c.__closure__[0].cell_contents) # 查看闭包的元素
1-1 闭包的意义 - 优先使用自己外层包裹的作用域
''' 闭包的意义: 返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域, 这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 ''' # 应用领域:延迟计算(原来我们是传参,现在我们是包起来) 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的前提下,为被装饰对象添加上新功能
运行原理:python解释器,一旦运行到@装饰器的名字,就会调用装饰器,然后将被装饰函数的内存地址,当做参数传给装饰器,最后将装饰器调用的结果赋值给原函数名
函数上添加多个装饰器的运行顺序:解释调用@语法的时候是自下而上运行(即从越靠近函数的执行器先先执行) 执行装饰器内部函数的wrapper的时候是自上而下
即,func = @outter_最外层(@outter_中间(@outter_内层))
2-1 不使用装饰器实现装饰器的功能 - 使用闭包
import time # 原函数index(),无参 def index(): print('welcome to index page') time.sleep(3) # 原函数home(name) 有参 def home(name): print('welcome to index page') time.sleep(2) return 123 # 闭包函数,用来接收index的地址并添加新功能之后返回 def outter(func): def wrapper(*args, **kwargs): # 让传入的参数 原封不动,则可以解决原函数对参数(有参无参)的要求 start = time.time() res = func(*args, **kwargs) # 运行形参函数,并且将func的返回值赋给res stop = time.time() print('run time is %s' % (stop - start)) return res # 返回传入函数的返回值 return wrapper index = outter(index) # 将index地址传入putter,返回wrapper地址 # 当前重新复制的index保存着wrapper的地址 index() home = outter(home) # home=outter(最原始那个home函数的内地址) #home=wrapper函数的内地址 res = home('egon') # res=wrapper函数的内地址('egon') print(res)
2-2 无参装饰器(@装饰器名)
import time def timmer(func): def wrapper(*args, **kwargs): # 让传入的参数 原封不动,则可以解决原函数对参数(有参无参)的要求 start_time = time.time() # 开始时间 res = func(*args, **kwargs) # 运行形参函数,并且将func的返回值赋给res stop_time = time.time() # 结束时间 print('run time is %s' % (stop_time - start_time)) print(res) return res # 返回传入函数的返回值 return wrapper @timmer # 调用timmer(foo)并赋值给foo,即foo = timmer(foo) def foo(): time.sleep(3) print('from foo') # 在foo()函数上套上timmer的无参装饰器 foo() # foo = timmer(foo)
2-2-1 无参装饰器 - 书写格式
''' def outter(func): def wrapper(*args,**kwargs) … 逻辑代码块 … res = func(*args,**kwargs) … 逻辑代码块 … return res return wrapper @outter def index(): pass index() '''
2-2-2 无参装饰器练习 - 认证功能装饰器
def login_out(func): def login_wrapper(*args, **kwargs): res = func(*args, **kwargs) return res return login_wrapper def users_list(): # 返回值为users_list users_list = [] with open('user_and_pwd', 'rt', encoding='utf-8') as users_pwd_f: users_date = users_pwd_f.read() user_list = users_date.split('/') user_list.remove('') for i in range(len(user_list)): users_name = user_list[i].split('|') users_list.append(users_name) print(users_list) return (users_list) def login_out(func): def login(*args, **kwargs): # 登陆判断 三次尝试机会 返回值: '登陆用户名' res = func(*args, **kwargs) count = 0 uname_dic = {} for n in users_list(): uname_dic[n[0]] = n[1] # print(uname_dic) while count < 3: name = input('请输入用户名:').strip() if name in uname_dic: pwd = input('请输入密码:').strip() if pwd == uname_dic[name]: print('登陆成功!\n') uname = name login = True break else: print('用户名不存在!') count += 1 # return (name) print(res) return res # 接受到原函数的返回值 return login @login_out def index(): print('认证功能装饰器') res = 123 return res index()
2-3 有参装饰器(@装饰器名(参数名))
def auth(driver='file'): def auth2(func): def wrapper(*args, **kwargs): name = input("user: ") pwd = input("pwd: ") if driver == 'file': if name == 'egon' and pwd == '123': print('login successful') res = func(*args, **kwargs) return res elif driver == 'ldap': print('ldap') return wrapper return auth2 @auth(driver='file') # 相等于@auth2 但在其内部可以使用dirver的值 def foo(name): print(name) foo('egon')
2-3-1 有参装饰器书写格式
# 在无参装饰器的外层再包一层函数,最外层函数用来进行往内层传值 ''' 有参装饰器(书写格式,做多三层) def out(d='123',a=2,) def outter(func): def wrapper(*args,**kwargs) … 逻辑代码块 … res = func(*args,**kwargs) … 逻辑代码块 … return res return wrapper @out(d='1111') def index(): pass index() '''
2-4 函数上叠加 多装饰器 的运行顺序
''' 解释调用@语法的时候是自下而上运行(即从越靠近函数的执行器先先执行) 执行装饰器内部函数的wrapper的时候是自上而下 即,func = @outter_最外层(@outter_中间(@outter_内层)) ''' import time def outter1(func1): # func1=wrapper2 print('outter1') def wrapper1(*args, **kwargs): print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 def outter2(func2): # func2=最原始的那个index的内存地址 print('outter2') def wrapper2(*args, **kwargs): print('wrapper2') res2 = func2(*args, **kwargs) return res2 return wrapper2 @outter1 # index=outter1(wrapper2) #index=wrapper1 @outter2 # outter2(最原始的那个index的内存地址) ===> wrapper2 def index(): print('welcome to index page') time.sleep(3) index() # wrapper1() ''' outter2 outter1 wrapper1 wrapper2 '''
2-5 @wraps 对装饰器内层函数的使用 -- 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性
# ------ 不对内层函数使用 @ wraps() - --- import time def outter1(func1): # print('outter1') def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 显示装饰器内层函数名字 print(index.__doc__) # 显示装饰器的注释内容 ''' 使用装饰器之后 会显示 装饰器重新复制的函数对象数据 >>>wrapper1 >>>None ''' # ------ 对内层函数使用 @ wraps() - ------------------------ import time from functools import wraps def outter1(func1): # 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性 # 若有多层函数嵌套,只需要在最内层的装饰器添加 @wraps @wraps(func1) def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 显示函数名字 print(index.__doc__) # 显示函数的注释内容 ''' >>>index >>> >>> index 功能 >>> '''
三、 递归 - 调用函数的过程中直接或者间接调用本身
''' 函数的递归 : 函数嵌套调用的一种特殊形式,在调用函数的过程中直接或者间接调用本身 递归调用必须有两个明确的阶段: 1.回溯:一次次递归调用下去,即一重复的过程 但每次重复,问题的规模减少,直到逼近最终结果 即回溯结果一定有一个明确的结束条件 2.递推:一层一层的往回传值 ''' l = [1, [2, [3, [4, [5, [6, ]]]]]] def s(l): for i in l: if type(i) is not list: print(i) else: s(i) s(l)
3-1 二分法
nums = [13, 15, 17, 23, 31, 54, 87, 99, 104, 230] def func(num, search): print(num) if len(num) == 0: print('can not find it') return middle = len(num) // 2 if num[middle] > search: func(num[:middle], search) elif num[middle] < search: func(num[middle:], search) else: print('find it') return func(nums, 3) ''' [13, 15, 17, 23, 31, 54, 87, 99, 104, 230] [13, 15, 17, 23, 31] [13, 15] [13] [] can not find it ''' l = [1, 2, 10, 30, 33, 99, 101, 200, 301, 402] def search(num, l, start=0, stop=len(l) - 1): if start <= stop: mid = start + (stop - start) // 2 print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' % (start, stop, mid, l[mid])) if num > l[mid]: start = mid + 1 elif num < l[mid]: stop = mid - 1 else: print('find it', mid) return search(num, l, start, stop) else: # 如果stop > start则意味着列表实际上已经全部切完,即切为空 print('not exists') return search(301, l) ''' start:[0] stop:[9] mid:[4] mid_val:[33] start:[5] stop:[9] mid:[7] mid_val:[200] start:[8] stop:[9] mid:[8] mid_val:[301] find it 8 '''