装饰器
参考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)