装饰器详解

一、nonlocal关键词

1、作用:将local和enclosing(e中的名字需要提前定义)中的名字统一

2、应用场景:如果想在被嵌套的函数中修改外部函数变量(名字)的值

3、案例

def outer():
    num = 0
    print(num)       # 结果为  0
    def inner():     # 如果想在被嵌套的函数中修改外部函数变量(名字)的值
        nonlocal num # 将L与E(E中的名字需要提前定义)的名字统一
        num = 100
        print(num)   # 结果为 100
    inner()
    print(num)       # 结果为 100
outer()
num =100
def fn():
    global num
    num = 10
def fn2():
    global num
    num = 20
fn2()
fn()
print(num)

 

 

二、开放封闭原则

1、定义:不能修改源代码,不能更改调用方式,但可以对外提供增加新功能的特性

2、开放:修改了源代码,没有更改调用方式,对外调用方式还是原来的,但功能有所增加,不能更改被装饰对象(函数)的调用方式,且达到了增加功能的效果

def fn():
    print('fn run1')
    print('fn run')
    print('fn run3')
fn()

3、封闭:没有修改源代码,但更改了调用方式 。

不能修改被被装饰对象(函数)的源代码

def fn():
    print('fn run')
fn()
def wrap(fn):
    print('fn run1')
    fn()
    print('fn run3')
    fn()
wrap(fn)

 

三、装饰器

1、装饰器:闭包的一个应用场景。为一个函数添加新功能的工具

def vase():
    print('插花')
vase()              # 结果为 插花

# 增加一个绘画观赏功能:不满足封闭开放原则,修改了源代码
def vase():
    print('插花')
    print('绘画观赏')
vase()
# 结果为
# 插花
# 绘画观赏

# 增加一个绘画观赏功能,不满足开放封闭原则,更改了调用方式
def vase():
    print('插花')
vase()
def wrap(vase):
    vase()
    print('绘画观赏')
wrap(vase)
def vase():
    print('插花')

def wrap(vase):
    def fn():
        print('绘画观赏')
    return fn
vase = wrap(vase)
vase()
def vase():
    print('插花')

# 下方的函数嵌套结构就是装饰器*****
def wrap(x): #x=vase
    def fn():
        x()  # 原有的vase
        print('绘画:进行观赏')
    return fn          # 拓展功能后的vase
vase = wrap(vase)      # 将拓展功能后的功能函数重新赋值给vase
vase()                 # 功能拓展了,且调用方式不变

2、装饰器简化语法

def outer(fn):
    def inner():
        fn()
        print('绘画观赏')
    return inner
def wrap(fn):
    def inner():
        fn()
        print('摆放功能')
    return inner
# 语法糖|笑笑语法
@wrap            # 总结:一个函数可以被任意一个装饰器装饰,也可以被任意几个装饰器装饰
@outer           #       装饰的顺序会影响新增功能的执行顺序

def vase():
    print('插花')
vase()
# 结果为
# 插花
# 绘画观赏
# 摆放功能

 

四、有参有反的函数被装饰

案例

# 增加一个账号处理功能: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)

 

五、登录认证案例

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()

 

五、案例

1、写出完整的装饰器(不用考虑带参装饰器,就是普通装饰器)语法

def wrapper(func):
    def inner(*args, **kwargs):
        pass
        result = func(*args, **kwargs)
        pass
        return result
    return inner

2、有一个计算两个数和的方法,为其添加一个确保两个参数都是int或float类型的装饰器,保证运算不会抛异常

def check_num(func):
    def inner(n1, n2):
        b1 = isinstance(n1, int) or isinstance(n1, float)
        b2 = isinstance(n2, int) or isinstance(n2, float)
        if not (b1 and b2):
            print('不能求和')
            return  # 结束掉,不让其进入计算功能
        return func(n1, n2)
    return inner

@check_num
def add(n1, n2):
    return n1 + n2

print(add(1, '2'))

3、有一个一次性录入人名并返回人名的方法(人名只考虑存英文),为其添加一个装饰器,使得处理后人名首字母一定大写

def upper_name(func):
    def inner():
        result = func()
        return result.title()  # Owen
    return inner

@upper_name
def get_name():
    name = input('name: ')  # owen
    return name

print(get_name())

 

posted @ 2019-04-03 19:33  TianShu  Views(169)  Comments(0Edit  收藏  举报