装饰器

参考https://www.runoob.com/w3cnote/python-func-decorators.html
简洁记录一些对我有用的笔记

python的装饰器是通过闭包的方式实现
先理解了第一段代码,函数式实现的闭包,再看文字

特点:
函数内部嵌套函数
函数内部可以引用外部的变量

闭包是有权限访问其他函数作用域的局部变量的一个函数
闭包的本质是连接函数内部与外部的桥梁
闭包是能访问其他函数内部局部变量的(内部)函数

闭包在JS中的解释
函数内变量的有效范围就是函数作用域,在函数执行后作用域就会被清理、内存也随之被收回,但是由于闭包是建立在一个函数内部的子函数,该函数可访问所在函数内部的所有变量,以及该函数可访问的全局变量,又叫上级作用域,即使上级函数执行完,作用域也不会随之销毁,这时的子函数—也就是闭包,仍然可以访问上级作用域的变量。

由于闭包可以缓存上级作用域,那么就使得函数外部(在某处调用该函数内部定义的子函数,子函数内部可访问该函数内部变量)打破了“函数作用域”的束缚,可以访问函数内部的变量。
以平时使用的 AJAX 成功回调为例,这里其实就是个闭包,由于上述的特性,
回调就拥有了整个上级作用域的访问和操作能力,提高了极大的便利。
开发者不用去学钩子函数来操作上级函数作用域内部的变量了

python 函数闭包
内部定义个新的函数,新的函数执行传入的函数,即被包装的函数,在其前后加入一些代码段如日志、异常处理、权限检查
返回新的函数

from functools import wraps
def decorator_name(f):
    @wraps(f) 
    # 修改装饰后的函数名为传进来的函数名,函数信息不变
    # 否则在打印该函数的信息时将会是decorator_name而不是传入的f
    def decorated_f(*args, **kwargs):
        if not can_run: # 访问外部变量
            return "Function will not run"
        return f(*args, **kwargs) # 访问decorator_name函数的参数f
    return decorated_f
 
# 相当于语句func=decorator_name(func)
@decorator_name # 注意这里没有圆括号
def func():
    return("Function is running")
 
can_run = True
print(func())
# Output: Function is running
 
can_run = False
print(func())
# Output: Function will not run

可接收参数的装饰器

from functools import wraps
 
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile, 'a') as opened_file:
                # 现在将日志打到指定的logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
 
# 注意,这里有个圆括号,表示的是调用,括号里可以填参数,见下面,实际上相当于@logging_decorator
@logit() 
def myfunc1():
    pass
 
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
 
@logit(logfile='func2.log')
def myfunc2():
    pass
 
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

通过类实现装饰器,更强大

from functools import wraps
 
class logit(object):
    # 初始化
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
    
    # 调用
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile并写入
            with open(self.logfile, 'a') as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string + '\n')
            # 现在,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做别的
        pass

使用方法和函数的装饰器一样@logit()
注意有括号
相当于执行func = logit(func)

继承logit装饰器,添加邮件发送功能

class email_logit(logit):
    '''
    一个logit的实现版本,可以在函数调用时发送email给管理员
    '''
    def __init__(self, email='123@gmail.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)
 
    def notify(self):
        # 发送一封email到self.email
        # 这里没有实现
        pass

类实现装饰器,包装类内的方法

from functools import wraps
class error_key_check2:
    def __init__(self, caller_note):
        # caller_note提示是哪个类调用的,好发现错误
        self.caller_note = caller_note

    def __call__(self, func):
        @wraps(func)
        def decorated_func(self2, *args, **kwargs):
            # 要区别两个self
            print(self.caller_note, func.__name__, "被调用")
            if 'error' in args[0].keys():
                print(func, 'data中含有error字段,有出错,请检查')
                return -1
            else:
                return func(self2, *args, **kwargs)
        return decorated_func


class dog:
    @error_key_check2('dog')
    def bark(self, data):
        print('bark', data)
posted @ 2023-01-29 15:51  ecnu_lxz  阅读(13)  评论(0编辑  收藏  举报