装饰器详解

一、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 @   困了就睡觉觉  Views(173)  Comments(0Edit  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示