函数之装饰器的使用

nonlocal关键字

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)

装饰器

'''
装饰器:就是闭包(闭包的一个应用场景)
	-- 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
	
优点:
	-- 丰富了原有函数的功能
	-- 提高了程序的可拓展性
'''

开放封闭原则 : 不改变调用方式与源代码上增加功能

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

装饰器的简单实现

def outer(func):
    def inner():
        print("新增功能1")
        func()
        print("新增功能2")
    return inner

def func():
    print("原有功能")
    
func = outer(func)

# 花瓶

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()  # 功能拓展了,且调用方式不变

装饰器语法糖

def outer(func):
    def inner():
        print("新增功能1")
        func()
        print("新增功能2")
    return inner

@outer
def func():
    print("原有功能")
    
# 总结:一个函数可以被任意一个相关装饰器装饰,也可以被任意几个装饰器装饰
# 注:装饰的顺序会影响新增功能的执行顺序


装饰有参有返的函数

def outer(func):
    def inner(*args, **kwargs):
        print("新增功能1")
        result = func(*args, **kwargs)
        print("新增功能2")
        return result
    return inner

@outer
def func(*args, **kwargs):
    print("原有功能")
    return "原有结果"
# 增加一个账号处理功能: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)

装饰器结合可变长参数

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

# 登录功能
@check_usr
def login(usr, pwd):
    if usr == 'abc' and pwd =='123qwe':
        print('登录成功')
        return True
    print('登录失败')
    return False

res = login('abc', '123qwe')  # inner
print(res)

def wrap(fn):
    def inner(*args, **kwargs):
        # print(args)
        # print(kwargs)
        result = fn(*args, **kwargs)
        print('新增功能')
        return result

    return inner


@wrap
def func(a, b, c, *, x, y, z):
    print(a, b, c, x, y, z)
    print('原有功能')


func(1, 2, 3, x=10, y=20, z=30)

有参装饰器

def wrap(arg):
    def outer(func):
        def inner(*args, **kwargs):
            print("新增功能1")
            result = func(*args, **kwargs)
            print("新增功能2")
            return result
        return inner

@wrap("装饰器参数")
def func(*args, **kwargs):
    print("原有功能")
    return "原有结果"

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

登录认证案例

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

wraps修改函数文档注释

from functools import wraps
def outer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        '''装饰器文档注释'''
        func(*args, **kwargs)
    return inner

@outer
def func(*args, **kwargs):
    '''原有文档注释'''
    print("原有功能")
原文地址:https://www.cnblogs.com/fuwei8086/p/10665253.html