修饰器

功能

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

首先看这样一段代码

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使用修饰器的更多相关文章

  1. [转载]python 详解re模块

    原文地址:python 详解re模块作者:Rocky 正则表达式的元字符有. ^ $ * ? { [ ] | ( ) .表示任意字符 []用来匹配一个指定的字符类别,所谓的字符类别就是你想匹配的一个字 ...

  2. 33 Python 详解命令解析 - argparse--更加详细--转载

    https://blog.csdn.net/lis_12/article/details/54618868 Python 详解命令行解析 - argparse Python 详解命令行解析 - arg ...

  3. HTTPS 详解一:附带最精美详尽的 HTTPS 原理图

    HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 前言 作为一个有追求的程序员,了解行业发展趋势和扩充自己的计算机知识储备都是很有 ...

  4. Lucene系列三:Lucene分词器详解、实现自己的一个分词器

    一.Lucene分词器详解 1. Lucene-分词器API (1)org.apache.lucene.analysi.Analyzer 分析器,分词器组件的核心API,它的职责:构建真正对文本进行分 ...

  5. jQuery Validate验证框架详解(jquery.validate.min.js)

    原博客 jQuery Validate验证框架详解 jQuery校验官网地址:https://jqueryvalidation.org/ 一.导入js库 <script type="t ...

  6. 定时器详解和应用、js加载阻塞、css加载阻塞

    1.setTimeout().setInterval()详解和应用 1.1 详解: setTimeout.setInterval执行时机 1.2 存在问题: setInterval重复定时器可能存在的 ...

  7. Firebug控制台详解,让调试js代码变得更简单

    http://www.open-open.com/lib/view/open1373120100347.html Firebug是网页开发的利器,能够极大地提升工作效率. Firebug控制台详解 控 ...

  8. Struts2各个功能详解(2)-输入校验和拦截器

    前面知道了struts2的架构图和struts2的自动封装表单参数和数据类型自动转换,今天来学struts2的第三第四个东西,输入校验和拦截器.  一:输入校验 客户端校验进行基本校验,如检验非空字段 ...

  9. C# 9.0新特性详解系列之一:只初始化设置器(init only setter)

    1.背景与动机 自C#1.0版本以来,我们要定义一个不可变数据类型的基本做法就是:先声明字段为readonly,再声明只包含get访问器的属性.例子如下: struct Point { public ...

随机推荐

  1. React.createClass vs. ES6 Class Components

    1 1 1 https://www.fullstackreact.com/articles/react-create-class-vs-es6-class-components/ React.crea ...

  2. 如何用 js 实现一个 call 函数

    如何用 js 实现一个 call 函数 原理 实现方式 总结 refs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referenc ...

  3. 「NGK每日快讯」12.3日NGK公链第30期官方快讯!

  4. springCloud中的注册中心Nacos

    springCloud中的注册中心Nacos 三个模块: 1.注册中心 2.服务提供者(生产者) 提供服务 3.服务消费者(消费者)调用服务 流程:消费者和生产者都要向注册中心注册,注册的是二者中服务 ...

  5. C++ Primer Plus 第一章 预备知识

    C++ Primer Plus 第一章 预备知识 知识点梳理 本章主要讲述了C++的由来,讨论了面向过程语言与面向对象语言的区别,介绍了ANSI/ISO制定的C++标准,阐述了在Windows.Mac ...

  6. 微信小程序切换标签改变样式

    微信小程序切换标签改变样式 wxml <!--顶部导航栏--> <view class="swiper-tab"> <view class=" ...

  7. Java RPC 框架 Solon 1.3.7 发布,增强Cloud接口能力范围

    Solon 是一个微型的Java RPC开发框架.项目从2018年启动以来,参考过大量前人作品:历时两年,4000多次的commit:内核保持0.1m的身材,超高的跑分,良好的使用体验.支持:RPC. ...

  8. 项目管理之Git

    @[TOC]( Git命令:分支与合并)Git一款很好的项目版本管理工具,更是一款优秀的分布式项目管理工具.今天主要给大家介绍Git 强大的分支和合并功能,分支和合并可以说在实际的工作当中用到的是最多 ...

  9. AOP面试造火箭始末

    本文已整理致我的github地址,欢迎大家 star 支持一下 这是一个困扰我司由来已久的难题,Dubbo 了解过吧,对外提供的服务可能有多个方法,一般我们为了不给调用方埋坑,会在每个方法里把所有异常 ...

  10. 利用CORDIC算法计算三角函数

    这里主要先介绍如何利用CORDIC算法计算固定角度\(\phi\)的\(cos(\phi)\).\(sin(\phi)\)值.参考了这两篇文章[1].[2]. 一般利用MATLAB计算三角函数时,用\ ...