# day12
## 复习
```python
1.函数对象:函数名
def fn(): pass
1) f = fn
2) def fun(fn): pass
3) def func(): return fn
4) dic = {'函数': fn} | ls = [fn]
2.名称空间与作用域
LEGB
len("abc")
len = 10
del len
len("abc")
global关键字 L => G
num = 100
def fn():
global num
num = 10
def fn1():
global
num = 20
fn1()
fn()
print(num)
3.函数嵌套定义与闭包
def f1():
num = 10
def f2():
print(num)
def f1():
def f2():
def f3():
def f4():
pass
什么是闭包:f2,定义在函数内部的 函数
# 今天
# 装饰器:就是闭包的一种应用
```
## nonlocal关键字
```python
# 作用:将 L 与 E(E中的名字需要提前定义) 的名字统一
# 应用场景:如果想在被嵌套的函数中修改外部函数变量(名字)的值
# 案例:
def outer():
num = 10
print(num) # 10
def inner():
nonlocal num
num = 20
p77rint(num) # 20
inner()
print(num) # 20
'''
# num = 0
# def fn():
# global num # L => G, 将局部名字与全局名字统一
# num = 20
# fn()
# print(num)
def outer():
# global num
num = 0
def inner():
# 如果想在被嵌套的函数中修改外部函数变量(名字)的值
nonlocal num # 将 L 与 E(E中的名字需要提前定义) 的名字统一
num = 10
print(num)
inner()
print(num)
outer()
# print(num)
'''
```
## 开放封闭原则:不改变调用方式与源代码上增加功能
```python
'''
1.不能修改被装饰对象(函数)的源代码(封闭)
2.不能更改被修饰对象(函数)的调用方式,且能达到增加功能的效果(开放)
'''
# 1.0版本
def fn():
print('fn run')
# fn()
# 2.0版本
# def fn():
# print('fn run0')
# print('fn run1')
# print('fn run2')
# fn()
# 修改了源代码,没有更改调用方式,对外调用方式还是原来的,但功能要有所增加(开放)
# def fn():
# print('fn run0')
# print('fn run')
# print('fn run2')
# fn()
# 更改了调用方式,没有修改原功能代码(封闭)
def wrap(fn):
print('fn run0')
fn()
print('fn run2')
wrap(fn)
'''
1.不能修改被装饰对象(函数)的源代码(封闭)
2.不能更改被修饰对象(函数)的调用方式,且能达到增加功能的效果(开放)
'''
```
## 装饰器
```python
# 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
# 被装饰的函数:fn
# 外层函数:outer(func) outer(fn) => func = fn
# 替代版函数: return inner: 原功能+新功能
def fn():
print("原有功能")
# 装饰器
def outer(tag):
def inner():
tag()
print(新增功能")
return inner
fn = outer(fn)
fn()
# 花瓶
# def vase():
# print('插花')
# vase()
# 增加一个绘画后观赏功能:不满足开放封闭原则,修改了源代码
# def vase():
# print('插花')
# print('绘画:进行观赏')
# vase()
# 增加一个绘画后观赏功能:不满足开放封闭原则,修改了调用方式
# def wrap(fn):
# vase()
# print('绘画:进行观赏')
# wrap(vase)
# 虽然满足了开放封闭原则,但是出现了函数调用的死循环
# def fn():
# vase()
# print('绘画:进行观赏')
# vase = fn
# vase()
# 了解:满足开放封闭原则,且可以达到装饰器的作用:拓展功能
# def vase():
# print('插花')
# tag = vase # 暴露在全局:很容易被修改掉
# def fn():
# tag()
# print('绘画:进行观赏')
# vase = fn
# vase()
# def wrap(tag):
# def fn():
# tag()
# print('绘画:进行观赏')
# return fn
# vase = wrap(vase)
# vase()
def vase():
print('插花')
# 下方的函数嵌套结构就是装饰器
def wrap(tag):
def fn():
tag() # 原有的vase
print('绘画:进行观赏')
return fn # 拓展功能后的vase
vase = wrap(vase) # 将拓展功能后的功能函数重新赋值给vase
vase() # 功能拓展了,且调用方式不变
```
## @语法糖: @外层函数
```python
def outer(f):
def inner():
f()
print("新增功能1")
return inner
def wrap(f):
def inner():
f()
print("新增功能2")
return inner
@wrap # 被装饰的顺序决定了新增功能的执行顺序
@outer # <==> fn = outer(fn): inner
def fn():
print("原有功能")
def outer(fn):
def inner():
fn()
print("绘画:进行观赏")
return inner
def wrap(fn):
def inner():
fn()
print('摆放功能')
return inner
# 语法糖 | 笑笑语法
@wrap
@outer # <==> vase = outer(vase)
def vase():
print('插花')
# vase = outer(vase)
vase()
# 总结:一个函数可以被任意一个相关装饰器装饰,也可以被任意几个装饰器装饰
# 注:装饰的顺序会影响新增功能的执行顺序
```
## 有参有返的函数被装饰
```python
def check_usr(fn): # fn, login, inner:不同状态下的login,所以参数是统一的
def inner(usr, pwd):
# 在原功能上添加新功能
if not (len(usr) >= 3 and usr.isalpha()):
print('账号验证失败')
return False
# 原有功能
result = fn(usr, pwd)
# 在原功能下添加新功能
# ...
return result
return inner
@check_usr
def login(usr, pwd):
if usr == 'abc' and pwd =='123qwe':
print('登录成功')
return True
print('登录失败')
return False
# 总结:
# 1.login有参数,所以inner与fn都有相同参数
# 2.login有返回值,所以inner与fn都有返回值
"""
inner(usr, pwd):
res = fn(usr, pwd) # 原login的返回值
return res
login = check_usr(login) = inner
res = login('abc', '123qwe') # inner的返回值
"""
原版
# 增加一个账号处理功能:3位及以上英文字符或汉字
def check_usr(fn):
def inner(usr, pwd):
if not (len(usr) >= 3 and usr.isalpha()):
print('账号验证失败')
return False
result = fn(usr, pwd) # login
return result
return inner
# 增加一个密码处理功能:6位及以上英文和数字
def check_pwd(fn):
def inner(usr, pwd):
if not (len(pwd) >= 6 and pwd.isalnum()):
print('密码验证失败')
return False
return fn(usr, pwd)
return inner
# 登录功能
@check_usr # login = check_usr(login) = inner
@check_pwd
def login(usr, pwd):
if usr == 'abc' and pwd =='123qwe':
print('登录成功')
return True
print('登录失败')
return False
res = login('abc', '123qwe') # inner
print(res)
```
## 装饰器最终写法
```python
def wrap(fn):
def inner(*args, **kwargs):
print('前增功能')
result = fn(*args, **kwargs)
print('后增功能')
return result
return inner
@wrap
def fn1():
print('fn1的原有功能')
@wrap
def fn2(a, b):
print('fn2的原有功能')
@wrap
def fn3():
print('fn3的原有功能')
return True
@wrap
def fn4(a, *, x):
print('fn4的原有功能')
return True
fn1()
fn2(10, 20)
fn3()
fn4(10, x=20)
```
## 带参装饰器:了解
```python
# 了解
def outer(input_color):
def wrap(fn):
if input_color == 'red':
info = ' 33[36;41mnew action33[0m'
else:
info = 'yellow:new action'
def inner(*args, **kwargs):
pass
result = fn(*args, **kwargs)
print(info)
return result
return inner
return wrap # outer(color) => wrap
color = input('color: ')
@outer(color) # @outer(color) ==> @wrap # func => inner
def func():
print('func run')
func()
```
## 登录认证功能
```python
is_login = False # 登录状态
def login():
usr = input('usr: ')
if not (len(usr) >= 3 and usr.isalpha()):
print('账号验证失败')
return False
pwd = input('pwd: ')
if usr == 'abc' and pwd =='123qwe':
print('登录成功')
is_login = True
else:
print('登录失败')
is_login = False
# 完成一个登录状态校验的装饰器
def check_login(fn):
def inner(*args, **kwargs):
# 查看个人主页或销售功能前:如果没有登录先登录,反之可以进入其功能
if is_login != True:
print('你未登录')
login()
# 查看个人主页或销售
result = fn(*args, **kwargs)
return result
return inner
# 查看个人主页功能
@check_login
def home():
print('个人主页')
# 销售功能
@check_login
def sell():
print('可以销售')
home()
```