Python(装饰器,递归函数)
一、开放封闭原则
软件面世时,不可能把所有的功能都设计好,再未来的一两年功能会陆续上线,定期更新迭代,软件之前所用的源代码,函数里面的代码以及函数的调用方式一般不会修改,可以在源码不改变的情况下,增加一些新的功能.
- 开放原则 : 在源码尽量不改变的情况下,更新增加一些额外的功能.
- 封闭原则 : 不要改变源码和调用方式
二、初识装饰器
装饰器是以功能为导向的,就是一个函数,在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能.
-
python中装饰器 : 完美的诠释开放封闭原则
-
装饰器就是一个函数 : 他要装饰一个函数,在不改变原函数的源码以及调用方式的前提下,给其增加一个额外的功能
import time def index(): time.sleep(2) # 模拟一下网络延迟以及代码的效率 print('欢迎访问博客园主页') def home(name): time.sleep(3) # 模拟一下网络延迟以及代码的效率 print(f'欢迎访问{name}主页') def timer(func): # func = index def inner(): start_time = time.time() func() end_time = time.time() print(f'此函数的执行效率为{end_time-start_time}') return inner index = timer(index) index() # 此为装饰器的雏形,虽然满足了开放封闭原则,但是如果当源代码又返回值时则此代码不够完善.
三、被装饰函数带返回值
当需要与原代码返回值一致的时候需要注意一下两点
-
明确源代码的返回值应该返回给谁
-
实际返回给了谁
-
如何修改
import time def index(): time.sleep(2) # 模拟一下网络延迟以及代码的效率 return '欢迎访问博客园主页' def timer(func): # func = index def inner(): start_time = time.time() ret = func() end_time = time.time() return ret return inner index = timer(index) # inner print(index()) # print(inner()) # 实际的返回值返给了inner,为了让返回值与源代码一致,需要进行赋值操作,这样就保证了返回值一致 # 现在代码已经满足原函数的返回值与装饰器之后的返回值保持一致了,还缺少的就是传参一致.
四、被装饰函数带参数的装饰器
当需要与原代码传参保持一致需要注意一下几点
-
明确源代码的传参应该传参给谁
-
实际传参给了谁
-
如何修改
import time def home(name,age): time.sleep(3) # 模拟一下网络延迟以及代码的效率 print(name,age) print(f'欢迎访问{name}主页') def timer(func): # func = home def inner(*args,**kwargs): # 函数定义时,*代表聚合:所以你的args = ('岁月',18) start_time = time.time() func(*args,**kwargs) # 函数的执行时,*代表打散:所以*args --> *('岁月',18)--> func('岁月',18) end_time = time.time() return inner home = timer(home) home('岁月',18) # 这样利用*的打散与聚合的原理,将这些实参通过inner函数的中间完美的传递到给了相应的形参。
五、标准版装饰器
-
代码优化 : 语法糖,Python给我们提供了一个简化机制,用一个很简单的符号去代替类似home = timer(home)这一句话。
注意 : 因为涉及函数的调用,@timer一定要放在被装饰函数的上方,否则会报错.
def timer(func): # func = home def inner(*args,**kwargs): start_time = time.time() func(*args,**kwargs) end_time = time.time() return inner @timer # 此处代替了home = timer(home) def home(name,age): time.sleep(3) # 模拟一下网络延迟以及代码的效率 print(name,age) print(f'欢迎访问{name}主页') home('岁月',18)
-
至此标准版的装饰器如下,完全符合代码开放封闭原则:
def warpter(f): def inner(*args,**kwargs) # 此处执行被装饰函数之前的操作 ret=f(*args,**kwargs) # 此处执行被装饰函数之后的操作 return ret return inner
六、带参数的装饰器
函数就应该具有函数传参功能,现在要完成的就是装饰器要分情况去判断账号和密码,不同的函数用的账号和密码来源不同,但是之前写的装饰器只能接受一个参数就是函数名,所以现在需要写一个可以接受参数的装饰器
@wrapper_out("qq")此时分以下两步执行
-
第一步先执行wrapper_out("qq")函数,得到返回值wrapper
-
第二步@与wrapper结合,形成装饰器@wrapper 然后在依次执行
def user_information(n): user_name = input("账号:").strip() user_password = input("密码:").strip() with open(n,encoding="utf-8")as f : for i in f: name,password=i.strip().split("|") if user_name==name and password==user_password: judge[n] = True return True else: return False def wrapper_out(n): def wrapper(f): def inner(*args,**kwargs): if judge[n]: ret = f(*args, **kwargs) return ret else: if user_information(n): ret = f(*args, **kwargs) return ret else: return "账号或密码错误" return inner return wrapper @wrapper_out("qq") def user_qq(): return "欢迎来到qq" @wrapper_out("tiktok") def user_tiktok(): return "欢迎来到tiktok" judge={"qq":False,"tiktok":False} print(user_qq()) print(user_qq()) print(user_tiktok()) print(user_tiktok())
七、多个装饰器装饰一个函数
def wrapper1(func1): # func1 = f原函数
def inner1():
print('wrapper1 ,before func') # 2
func1()
print('wrapper1 ,after func') # 4
return inner1
def wrapper2(func2): # func2 == inner1
def inner2():
print('wrapper2 ,before func') # 1
func2() # inner1
print('wrapper2 ,after func') # 5
return inner2
@wrapper2 # f = wrapper2(f) 里面的f == inner1 外面的f == inner2
@wrapper1 # f = wrapper1(f) 里面的f == func1 外面的 f == inner1
def f():
print('in f') # 3
f() # inner2()
八、递归函数
-
递归函数 : 函数调用方式为函数本身,这种函数叫递归函数,类似while循环.
-
注意 : 官网规定默认递归的最大深度1000次,如果你递归超过100次还没有解决这个问题,那么执意使用递归,效率很低。
-
函数表达式样式
def func(): print("太极") func() func()
-
举例说明
# 例子 l1 = [1,2,4,['你好','世界',34,[3,5,[11,3]]],[7, 28],36] def func(lis): for i in lis: if type(i)==list: func(i) else: print(i) func(l1)