装饰器

1.1闭包函数

定义:如果内函数使用外函数的局部变量,并且把外函数把内函数返回出来的过程叫闭包,里面的内函数是闭包函数

# 闭包函数的例子
# 概念:内函数使用外函数的局部变量,且外函数返回内函数的计算结果的过程叫闭包。
# 里面的内函数我们就称之为闭包函数
def outer_func(exp):
    x = 1
    def inner_func(base):
       return base**exp
    return inner_func
square1 =outer_func(2)
cub = outer_func(3)
print(square1(2))
print(cub(2))
总结:不用每次就传exp的值,我们可以少传入一个参数

1.2装饰器

定义:本质是一种特殊多的嵌套函数,他接受一个函数作为参数(该函数就是被装饰的函数),并返回一个新的函数。装饰器最大的作用让我们在不改变被装饰函数的代码的情况下添加新的功能。(自己理解:就是函数嵌套函数且参数也是函数,一般要修改函数内容我们才能实现修改函数功能,有了装饰器我们就不用动函数原来的内容,从而完成对功能的增加和删除等操作)

1.3装饰器的基本用法

# 定义一个功能函数 输出a
def func():
    print('a')
func()
# 此时如果想要增加功能 即输出b a c
# 有两种方式

# 方式1:直接在原来定义的函数直接添加功能
def func():
    print('b')
    print('a')
    print('c')
func()
# 方式二 采用装饰器即构造一个新的函数,将func函数作为参数传入,
# 定义f1函数增加功能,即嵌套的内函数
def decorator(f):
    def f1():
        print('b')
        f()
        print('c')
        # 易错 这里要求返回的是新函数,而不是对象f1(),刚开始我一直 return f1()
        # 触发类型错误 TypeError: 'NoneType' object is not callable
    return f1

def func():
    print('a')
# 注意这里只能传入函数变量名字,不能加括号,我们不是为了调用函数
func = decorator(func)
func()
总结:
执行过程:
1.首先进入函数入口func = decorator(func),此时装饰器函数的参数func函数,即f是func
2.进入内函数,打印b,返回f1;执行f(),进入func(),打印a,返回f1;打印c,装饰函数结束
3.其中要注意精髓:我们这里将decorator(func)返回值传给 这个func()函数,即每次返回值都调用这个函数,实现了不改变函数的情况下增加功能

1.4装饰器语法糖

可以让代码更简洁易读的语法特性,在python有许多语法糖比如:

列表推导式、字典推导式、集合推导式、链式比较、解包、装饰器、f-string

装饰器语法糖例子(减少了传参的过程)

# 方式二 采用装饰器即构造一个新的函数,将func函数作为参数传入,
# 定义f1函数增加功能,即嵌套的内函数
def decorator(f):
    def f1():
        print('b')
        f()
        print('c')
        # 易错 这里要求返回的是新函数,而不是对象f1(),刚开始我一直 return f1()
        # 触发类型错误 TypeError: 'NoneType' object is not callable
    return f1

def func():
    print('a')
    
 #这两句实现多的效果是一样的   
#@decorator
#func = decorator(func)

func()

1.5装饰带参数的函数

当装饰器带有参数的函数时,装饰器内部也需要接收参数,从而保证在装饰器里调用被装饰器函数时能够正确传递参数。

1.5.1装饰只有一个参数的函数

# 装饰器装饰带有一个参数的函数
import  time
def timer(f):
    def decorator(x):
        start=time.time()
        ret = f(x)
        print(time.time() - start)
        return ret
    return decorator


@timer
def my_func(x):
     time.sleep(x)
my_func(1)

1.5.1装饰未知参数数量的函数

# 装饰器装饰未知参数数量的函数,使用*args和**kwargs
import  time
def timer(f):
    def decorator(*args, **kwargs):
        start=time.time()
        ret = f(*args, **kwargs)
        print(time.time() - start)
        return ret
    return decorator


@timer
def my_func(x):
     time.sleep(x)

@timer
def add(x,y):
    time.sleep(1)
    print(x+y)
my_func(1)
add(2,3)

1.6带参数的装饰器

是指装饰器本身需要参数

# 带参数的装饰器
def func1(prefix):
    def decorator(func):
        def wrapper(*args,**kwargs):
            result = func(*args,**kwargs)
            return f'{result},{prefix}'
        return wrapper
    return decorator
# 使用装饰器并传递参数
@func1('how are you?')

def greet(name):
    return f'hello,{name}'
# greet = func1('how are you?')(greet) 这个语句和语法糖即@func1那句功能相同
# 调用被装饰的函数
print(greet('Alice'))

1.7装饰器嵌套

装饰器嵌套是指在python当中,一个函数可以被多个装饰器依次装饰。简而言之,就是在同一个函数上设置多个装饰器以实现不同功能的添加

# 装饰器的嵌套

def decorator_one(func):
    def wrapper_one(*args,**kwargs):
        print('Decorator one-before call')
        result = func(*args,**kwargs)
        print('Decorator one-after call')
        return result
    return wrapper_one
def decorator_two(func):
    def wrapper_two(*args,**kwargs):
        print('Decorator two-before call')
        result = func(*args,**kwargs)
        print('Decorator two-after call')
        return result
    return wrapper_two


@decorator_one
@decorator_two
def say_hello(name):
    print(f'hello ,{name}')
# say_hello = decorateor_one(decorator_two(say_hello))
say_hello('Alice')
 总结:
 1.装饰器执行顺序就是从下往上依次传递,即首先找与之最近的装饰器即@decorator_two,
 将返回的结果wrapper_two作为参数传递给装饰器@decorator_one,将最终返回的结果存在wrapper_one中
 2.我们在运行程序时顺序:从上往下依次执行

1.8类装饰器

可以将一个类作为装饰器,为被装饰的函数添加新的功能。类装饰器通过实现类的--call--方法,使得类的实例可以当做函数来调用,从而实现对其他函数的装饰。

class MyDecorator:
# 初始化函数要接受函数
   def __init__(self,func):
        self.func = func

   def __call__(self,*args,**kwargs):
    print('something is happening before the function is called.')
    result = self.func(*args,**kwargs)
    print('something is happening after the function is called.')
    return result
@MyDecorator
def say_hello(name):
    print(f'hello,{name}')
# 这里say_hello首先就指向call方法
say_hello('ally')

2.装饰器的常见应用

2.1记录日志

可以用来记录函数调用的的详细信息,包括调用时间,参数,返回值等

import logging
def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.basicConfig(filename='./app.log',level=logging.INFO,filemode='a',format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        logging.warning(f'calling function:{func.__name__} with args:{args} and kwargs:{kwargs}')
        result = func(*args,**kwargs)
        logging.warning(f'Function{func.__name__}returned:{result}')
        return result
    return wrapper
@log_decorator
def test():
    print('123')
test()

2.2性能检测

用来测量函数执行所需要的时间,帮助识别性能瓶颈

import time
def timer(func):
   def wrapper(*args,**kwargs):
       start_time = time.time()
       result=func(*args,**kwargs)
       end_time=time.time()
       print(f'{func.__name__}took{end_time-start_time:.10f}secods to run')
       return result
   return wrapper
@ timer
def process_list(my_list):
   for i in my_list:
       print(i)


my_list=list(range(100))
process_list(my_list)

2.3 权限验证

class User:
    def __init__(self,username,role):
        self.username=username
        self.role=role
# 装饰器,检查用户是否具有管理员权限
def admin_only(func):
    def wrapper(user,*args,**kwargs):
        if user.role!='admin':
            raise PermissionError(f"User :{user.username} is not authorized to perform this action.")
        return func(user,*args,**kwargs)
    return wrapper
# 使用装饰器的函数
@admin_only
def delete_user(user,user_to_delete):
    print(f'User {user_to_delete.username} has been deleted by {user.username}.')

# 测试代码
admin_user = User('yy','admin')
normal_user = User('Alice','normal')

# 尝试以管理员身份删除用户
delete_user(admin_user,normal_user)
# 此时执行wrapper函数

# 尝试以普通用户删除用户
try:
    delete_user(normal_user,admin_user)
except PermissionError as e:
    print(e)