Python 详解修饰器 附带 js使用修饰器
修饰器
功能
修饰器的主要功能是,在不改变已有代码的情况下,为某一个类,方法等扩展功能
首先看这样一段代码
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)
Python 详解修饰器 附带 js使用修饰器的更多相关文章
- [转载]python 详解re模块
原文地址:python 详解re模块作者:Rocky 正则表达式的元字符有. ^ $ * ? { [ ] | ( ) .表示任意字符 []用来匹配一个指定的字符类别,所谓的字符类别就是你想匹配的一个字 ...
- 33 Python 详解命令解析 - argparse--更加详细--转载
https://blog.csdn.net/lis_12/article/details/54618868 Python 详解命令行解析 - argparse Python 详解命令行解析 - arg ...
- HTTPS 详解一:附带最精美详尽的 HTTPS 原理图
HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 前言 作为一个有追求的程序员,了解行业发展趋势和扩充自己的计算机知识储备都是很有 ...
- Lucene系列三:Lucene分词器详解、实现自己的一个分词器
一.Lucene分词器详解 1. Lucene-分词器API (1)org.apache.lucene.analysi.Analyzer 分析器,分词器组件的核心API,它的职责:构建真正对文本进行分 ...
- jQuery Validate验证框架详解(jquery.validate.min.js)
原博客 jQuery Validate验证框架详解 jQuery校验官网地址:https://jqueryvalidation.org/ 一.导入js库 <script type="t ...
- 定时器详解和应用、js加载阻塞、css加载阻塞
1.setTimeout().setInterval()详解和应用 1.1 详解: setTimeout.setInterval执行时机 1.2 存在问题: setInterval重复定时器可能存在的 ...
- Firebug控制台详解,让调试js代码变得更简单
http://www.open-open.com/lib/view/open1373120100347.html Firebug是网页开发的利器,能够极大地提升工作效率. Firebug控制台详解 控 ...
- Struts2各个功能详解(2)-输入校验和拦截器
前面知道了struts2的架构图和struts2的自动封装表单参数和数据类型自动转换,今天来学struts2的第三第四个东西,输入校验和拦截器. 一:输入校验 客户端校验进行基本校验,如检验非空字段 ...
- C# 9.0新特性详解系列之一:只初始化设置器(init only setter)
1.背景与动机 自C#1.0版本以来,我们要定义一个不可变数据类型的基本做法就是:先声明字段为readonly,再声明只包含get访问器的属性.例子如下: struct Point { public ...
随机推荐
- Node.js Spider
Node.js Spider How To Write a Spider using JavaScript, in order to auto download some svg images for ...
- Node.js require 模块加载原理 All In One
Node.js require 模块加载原理 All In One require 加载模块,搜索路径 "use strict"; /** * * @author xgqfrms ...
- 微信小程序-生命周期图解
微信小程序-生命周期图解 小程序生命周期 App 生命周期 https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.htm ...
- Github history viewer
Github history viewer https://github.githistory.xyz/ https://github.com/pomber/git-history https://c ...
- JSDoc in action
JSDoc in action JSDoc https://jsdoc.app/index.html https://github.com/jsdoc/jsdoc $ npm -g jsdoc $ n ...
- flex layout & demos
flex layout & demos https://codepen.io/xgqfrms/pen/jjLPKN https://css-tricks.com/snippets/css/a- ...
- c++ string与wstring转换
wchar_t to char #include <comdef.h> const wchar_t* exepath = L"d:\\中文 路径\\中文 路径.exe" ...
- NGK的去中心化自治实践,更人性化的DAO
2020年,DeFi市场市场火爆的同时,引爆了流动性挖矿的市场.行业内对DAO的思考也在源源不断进行,特别项目治理通证发行之前,DAO的去中心化的治理理念,是区块链属性中的重要的一环,也已引发了不同项 ...
- 死磕Spring之IoC篇 - 深入了解Spring IoC(面试题)
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- 别再人云亦云了!!!你真的搞懂了RDD、DF、DS的区别吗?
几年前,包括最近,我看了各种书籍.教程.官网.但是真正能够把RDD.DataFrame.DataSet解释得清楚一点的.论据多一点少之又少,甚至有的人号称Spark专家,但在这一块根本说不清楚.还有国 ...