开放封闭原则
软件面世时不可能将所有的功能都设计好, 会定期进行更新迭代. 对于软件之前的源码一般不会修改, 对函数里面的代码以及函数的调用方式不会改变.
封闭原则: 不改变源码
开放原则: 更新增加一些额外的功能
而装饰器完美的诠释了开放封闭原则
初识装饰器
定义: 装饰器就是一个函数, 他要装饰一个函数, 在不改变原函数的源码及调用方式的前提下给其增加额外的功能
有一个函数, 现在要给其增加一个测试执行效率的功能
- def index():
- print('欢迎访问博客园')
第一步: 通过添加 time 模块进行测试
- import time
- def index():
- time.sleep(2)
- print('欢迎访问博客园首页')
- start_time = time.time()
- index()
- end_time = time.time()
- print(f'此函数的执行效率 {end_time-start_time}')
? 虽然这种方式完成了对此函数增加效率测试功能, 但当需要对多个对象增加此项功能时, 就会出现过多的代码重复
第三步: 通过能将功能封装在函数中来避免代码的重复
- import time
- def index():
- time.sleep(2)
- print('欢迎访问博客园首页')
- def test_time(x):
- start_time = time.time()
- x()
- end_time = time.time()
- print(f'此函数的执行效率 {end_time- start_time}')
- test_time(index)
? 虽然解决了代码重复问题, 并且在不改变源码的前提下完成了给函数添加了功能, 但改变了函数的调用方式
第四步: 通过闭包来达到使原函数的调用方式不变
- import time
- def index():
- time.sleep(2)
- print('欢迎访问博客园首页')
- def test_time(x):
- def inner():
- start_time = time.time()
- x()
- end_time = time.time()
- print(f'此函数的执行效率 {start_time-end_time}')
- return inner
- ret = test_time(index)
- ret()
? 此时虽然进行了闭包但是函数调用方式还是改变了
第五步: 将函数调用这一步直接赋给原函数的函数名
- import time
- def index():
- time.sleep(2)
- print('欢迎访问博客园首页')
- def test_time(x):
- def inner():
- start_time = time.time()
- x()
- end_time = time.time()
- print(f'此函数的执行效率 {start_time-end_time}')
- return inner
- index = test_time(index)
- index()
? 这样就完成了在不改变原函数源码以及调用方式的前提下, 给函数增加了一项功能
通过语法糖加装装饰器
- import time
- def test_time(x):
- def inner():
- start_time = time.time()
- x()
- end_time = time.time()
- print(f'此函数的执行效率 {start_time-end_time}')
- return inner
- @test_time
- def index():
- time.sleep(2)
- print('欢迎访问博客园首页')
- index()
被装饰的函数带返回值
? 当被装饰函数带返回值时, 由于装饰器实际执行的时 inner 函数, 而 inner 函数并没有返回值, 因此当被装饰函数带返回值时, 直接用上述装饰器的话得到的返回值就是 None.
? 所以应对装饰器进行升级, 对其增加返回值并且返回的应该是 index 函数的返回值
- import time
- def test_time(x):
- def inner():
- start_time = time.time()
- ret = x()
- end_time = time.time()
- print(f'此函数的执行效率 {start_time-end_time}')
- return ret
- return inner
- @test_time
- def index():
- time.sleep(2)
- print('欢迎访问博客园首页')
- return 666
- index()
被装饰的函数带参数
? 当函数带参数时, 同上面的原因, 由于 inner 函数缺少形参函数执行时会报错, 要使函数执行就需要给 inner 函数增加形参
- import time
- def test_time(x):
- def inner(*args,**kwargs):
- start_time =time.time()
- ret = x(*args,**kwargs)
- end_time = time.time()
- print(f'此函数的执行效率 {start_time-end_time}')
- return ret
- return inner
- @test_time
- def index(1,2,3):
- time.sleep(2)
- print('欢迎访问博客园首页')
- return 666
标准版装饰器
装饰器的格式:
- def warpper(f):
- def inner(*args,**kwargs):
- '''被装饰函数之前的操作'''
- ret = f(*args,**kwargs)
- '''被装饰函数之后的操作'''
- return ret
- return inner
带参数的装饰器
? 举例说明, 抖音: 绑定的是微信账号密码. qq: 绑定的是 qq 的账号密码. 你现在要完成的就是你的装饰器要分情况去判断账号和密码, 不同的函数用的账号和密码来源不同.
? 由于之前写的装饰器只能接受一个参数就是函数名, 所以现在要写一个可以接受参数的装饰器.
- def chioce(n):
- def wrapper(f):
- def inner(*args,**kwargs):
- if n== 'qq':
- user_name = input('请输入用户名:').strip()
- password = input('请输入密码:').strip()
- with open('qq',encoding = 'utf-8') as f1:
- for i in f1:
- user, pwd = i.strip().split('|')
- if user_name == user and pwd == password:
- ret = f(*args,**kwargs)
- return ret
- print('用户名密码错误')
- elif n == 'tiktok':
- user_name = input('请输入用户名:').strip()
- password = input('请输入密码:').strip()
- with open('qq',encoding = 'utf-8') as f1:
- for i in f1:
- user, pwd = i.strip().split('|')
- if user_name == user and pwd == password:
- ret = f(*args,**kwargs)
- return ret
- print('用户名密码错误')
- return inner
- return wrapper
- @chioce('qq')
- def qq():
- print('成功访问 qq')
- @chioce('tiktok')
- def tiktok():
- print('成功访问抖音')
- qq()
- tiktok()
由于中间有一部分是重叠代码可进行简化:
- def chioce(n):
- def wrapper(f):
- def inner(*args,**kwargs):
- user_name = input('请输入用户名:').strip()
- password = input('请输入密码:').strip()
- with open(n,encoding = 'utf-8') as f1:
- for i in f1:
- user, pwd = i.strip().split('|')
- if user_name == user and pwd == password:
- ret = f(*args,**kwargs)
- return ret
- else:
- print('用户名密码错误')
- return inner
- return wrapper
- @chioce('qq')
- def qq():
- print('成功访问 qq')
- @chioce('tiktok')
- def tiktok():
- print('成功访问抖音')
- qq()
- tiktok()
将文件名用传进去的参数 n 代替, 可通过 n 直接判断调用的使哪个函数的文件
多个装饰器装饰一个函数
有如下代码分析最后打印的结果:
- def wrapper1(func1): # func1 == f 函数
- def inner1():
- print('wrapper1,before func') # 2
- func1() # 3
- print('wrapper1,after func') # 4
- return inner1
- def wrapper2(fun2): # func2 == inner1
- def inner2():
- print('wrapper2,before func') # 1
- func2() # 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')
- f()
结果:
- wrapper2,before func
- wrapper1,before func
- in f
- wrapper1,after func
- wrapper2,after func
递归函数
递归函数: 函数或者其他代码都可以解决递归解决的问题, 但是递归在某些时候能出奇制胜的效果, 人理解函数, 神理解递归. 其实递归就是函数本身调用他自己
举例:
- def age(n):
- if n == 1:
- return 18
- else:
- return age(n-1)+2
- print(age(4))
结果:
24 # 18+2+2+2
可通过 sys.setrecursionlimit 查看电脑递归次数上限:
- import sys
- import sys
- print(sys.setrecursionlimit(10000000))
- def func(n):
- print(n)
- n += 1
- print(666)
- func(n)
- func(1)
将如下列表中的每项元素打印出来:
- l1 = [1, 3, 5, ['太白','元宝', 34, [33, 55, [11,33]]], [77, 88],66]
- def func(n):
- for i in n:
- if type(i) == list:
- fun(i)
- else:
- print(i)
- func(l1)
来源: http://www.bubuko.com/infodetail-3103860.html