修饰器

功能

修饰器的主要功能是,在不改变已有代码的情况下,为某一个类,方法等扩展功能

首先看这样一段代码

def foo():
for i in range(10):
print(i)
foo()
添加需求 打印日志

现在需要在不改变这段代码前提下,计算出这段代码的运行时间日志

import time

def log(fun):
print('开始时间:{}'.format(time.time()))
fun()
print('结束时间:{}'.format(time.time())) def foo():
for i in range(10):
print(i) log(foo)
优化调用方式 处理多次调用

我们将foo函数当成一个参数传递给了log(),在def log(fun)函数中,fun()就等同于foo(),好,现在可以显示运行的时间日志了,但是这样就改变了调用方式 需要使用log(foo)来调用,假设代码中使用了100次foo()现在需要打印每个foo()的运行日志,全部这样改是不现实的,我们需要优化一下

import time

def log(fun):
def run_log():
print('开始时间:{}'.format(time.time()))
fun()
print('结束时间:{}'.format(time.time())) return run_log def foo():
for i in range(10):
print(i) foo = log(foo)
foo()

现在我们改造了下代码,foo = log(foo)时,调用log()方法,内部定义了一个run_log()方法,然后返回给了foo,此时foo等于run_log,调用foo相当于调用run_log 这样,即实现了需要打印日志的需求,又可以不用去修改已有代码

传参

现在的foo()方法可以打印0到9数字,但是,由于业务需求变更,现在需要给foo()方法传递一个值,例如foo(100),就需要打印出0-99的数字,同时还要打印日志,所以我们需要再次优化代码

import time

def log(fun):
def run_log(num):
print('开始时间:{}'.format(time.time()))
fun(num)
print('结束时间:{}'.format(time.time())) return run_log def foo(num):
for i in range(num):
print(i) foo = log(foo)
foo(100)

根据之前的逻辑,我们已经知道新的foo()等于run_log(),所以我们给foo(100)传递100的值时,实际上等同于run_log(100),所以我们直接在def foo(num)中是接受不到的,我们需要在run_log(num)中接受到参数,传给fun(num),这样新的函数就可以接受参数了

返回参数

新的需求又来了,需要在foo()中返回一个所有数字的累加之和,而我们现有的foo函数实际上是run_log,所以我们再来改造一下代码

import time

def log(fun):
def run_log(num):
print('开始时间:{}'.format(time.time()))
info = fun(num)
print('结束时间:{}'.format(time.time()))
return info return run_log def foo(num):
add_num = 0
for i in range(num):
add_num += i
print(i)
return add_num foo = log(foo)
x = foo(100)
print(x)

由于现在的foo()等同于run_log(),run_log()中的fun()相当于foo(),所以foo中返回的值传到了info中然后我们把info返回,x就可以接受到从run_log中传递出来的参数

好了,到这里我们就实现了一个阉割版修饰器

通用性

假如需要你再给一个新的函数foo2打印日志, 代码如下

import time

def log(fun):
def run_log(num):
print('开始时间:{}'.format(time.time()))
info = fun(num)
print('结束时间:{}'.format(time.time()))
return info return run_log def foo(num):
add_num = 0
for i in range(num):
add_num += i
print(i)
return add_num def foo2(num, num2):
add_num = 0
for i in range(num, num2):
add_num += i
print(i)
return add_num foo = log(foo)
x = foo(100)
print(x)

由于我们的修饰器只能接收一个参数,而foo2需要两个参数,现有代码无法实现,所以我们要继续升级代码

import time

def log(fun):
def run_log(*args,**kwargs):
print('开始时间:{}'.format(time.time()))
info = fun(*args,**kwargs)
print('结束时间:{}'.format(time.time()))
return info return run_log def foo(num):
add_num = 0
for i in range(num):
add_num += i
print(i)
return add_num def foo2(num, num2):
add_num = 0
for i in range(num, num2):
add_num += i
print(i)
return add_num foo = log(foo)
foo2 = log(foo2)
x = foo(100)
x2 = foo2(50,100)
print(x)
print(x2)

我们使用了*args,这样就可以接收任意数量的参数,可以满足我们的所有需求

语法糖

但是每次使用前都要写一个 foo = log(foo)这样的赋值操作,代码并不美观 不符合python的代码风格,所以python给提供了一种语法糖,可以用@log来替代,修改后代码如下

import time

def log(fun):
def run_log(*args,**kwargs):
print('开始时间:{}'.format(time.time()))
info = fun(*args,**kwargs)
print('结束时间:{}'.format(time.time()))
return info return run_log @log
def foo(num):
add_num = 0
for i in range(num):
add_num += i
print(i)
return add_num @log
def foo2(num, num2):
add_num = 0
for i in range(num, num2):
add_num += i
print(i)
return add_num x = foo(100)
x2 = foo2(50, 100)
print(x)
print(x2)

我们只需要在定义函数时,在上面添加一句@修饰器名就相当于完成了函数名 = 修饰器名(函数名)这样的操作

现在就已经是一个标准的修饰器了

扩展

同理,我们用js也可以写一个修饰器

function log(fun) {
function run_log(...ags) {
console.log('==============')
let info = fun(...ags)
console.log('==============')
return info
} return run_log
} foo = log(foo)
function foo(num, num1) {
let x = 0
for (let i = num; i < num1; i++) {
console.log(i)
x += i
}
return x
} foo2 = log(foo2)
function foo2(num) {
let x = 0
for (let i = 0; i < num; i++) {
console.log(i)
x += i
}
return x
} foo(10, 100)
foo2(10)

最新文章

  1. lua中实现异步资源读写
  2. css新笔记
  3. Postman 安装及使用入门教程
  4. 原生javascript和jquery实现简单的ajax例子
  5. Request.Form()的使用
  6. Ubuntu 14.10 下sed命令详解
  7. Hibernate学习笔记(三)Hibernate生成表单ID主键生成策略
  8. final, finally, finalize的区别
  9. cf D. Sereja ans Anagrams
  10. oracle的部分增删查改
  11. sqlite基本用法
  12. flask 连接MogoDB数据库
  13. SpringMVC(十六) 处理模型数据之SessionAttributes
  14. Oracle创建pfile spfile 文件及其恢复
  15. metamask源码学习-controllers-network
  16. ubuntu14.04 提示 卷 文件系统根目录 仅剩余xxx的硬盘空间
  17. ip啊
  18. TcxGrid 去除&lt;No data to display&gt;
  19. Android-Selector不起作用
  20. phpexcel导出excel等比例缩放图片

热门文章

  1. CSS 滚动条宽度 All In One
  2. Fetch &amp; POST
  3. Dart Generic All In One
  4. Win/Mac 键位映射 &amp; 在 Mac 上更改“键盘”偏好设置
  5. Taro 物料市场
  6. css 设置多行文本的行间距
  7. js navigator.wakeLock 保持屏幕唤醒状态
  8. 同样是NGK官方推出的代币,SPC与BGV有何异同?
  9. std::vector与std::list效能对比(基于c++11)
  10. Sqoop 数据迁移工具