是时候写一下Python装饰器了。
装饰器记录好,就剩下多进程,多线程,携程了,前面还欠着re,不知道什么时候写,re实在太强大了。
装饰器,记得第一次看到啥都不懂,也不知道为什么要设计这玩意,后面接触了Flask,Django才知道那些所谓的中间件都是一些装饰器。
首先,Python是万物皆对象,所以一个函数作为对象实在太正常,但刚接触Python的时候,我至少很长时间不能接受这种行为。
首先上一个最简单的装饰器:
# -*- coding:utf-8 -*- import random def check(func): print('I am func,I will start first') def wrap(*args, **kwargs): res = func(*args, **kwargs) if res > 0: return res else: return 0 print('I go out') return wrap @check def foo(m, n ): return random.randint(m, n)
这是一个极其普通的装饰器,就是负数输出为0。
@check所谓的语法糖很有意思,你不执行check或者foo的任意一个函数,单还是会输出
I am func,I will start first I go out
这个说明check函数已经执行了,check函数返回的是另一个需要被装饰的函数wrap。这里面到底发生了什么。
当用了@check的语法糖以后,其实在执行该脚本时,Python内部先执行了foo = check(foo)
所以,check函数会执行,而且foo也变成了check函数执行返回的wrap.
print(foo.__name__)
wrap
在前面代码中,如果打印foo的名字代码,就会发现foo的名字都已经变了。
现在我给foo与wrap都加上一些docstring的说明。
import random def check(func): print('I am func,I will start first') def wrap(*args, **kwargs): '''My name is wrap,hello''' res = func(*args, **kwargs) if res > 0: return res else: return 0 print('I go out') return wrap @check def foo(m, n ): '''My name is foo,hello''' return random.randint(m, n) print('foo_docstring---->',foo.__doc__)
foo_docstring----> My name is wrap,hello
从执行可以看出foo的docstring都已经发生了改变,这样对原函数实在太不友好了,不是说装饰器不影响原函数的执行,单原函数的一些内部属性发送了变化,装饰器你也太霸道了。
Python想到了这些,有一个原装的模块可以解决。
import random
import functools
def check(func):
print('I am func,I will start first')
@functools.wraps(func)
def wrap(*args, **kwargs):
'''My name is wrap,hello'''
res = func(*args, **kwargs)
if res > 0:
return res
else:
return 0
print('I go out')
return wrap
@check
def foo(m, n ):
'''My name is foo,hello'''
return random.randint(m, n)
print('foo_docstring---->',foo.__doc__)
print(foo(2, 6))
print('foo_name---->',foo.__name__)
foo_docstring----> My name is foo,hello 3 foo_name----> foo
从输出可以看出foo的内部属性在装饰器装饰后,没有发生任何改变。
接下来,我说一下多层装饰器,理解了这个多那些框架的中间件应该有更好的理解,至少我是这么认为的。
多层装饰器就是有多个@,这个多层装饰器,其实有那么一点点递归的感觉。
def deco1(func): print("This is deco1") def wrap1(*args, **kwargs): print('enter deco1:') result = func(*args, **kwargs) print('exit deco1') return result return wrap1 def deco2(func): print("This is deco2") def wrap2(*args, **kwargs): print('enter deco2') result = func(*args, **kwargs) print('exit deco2') return result return wrap2 def deco3(func): print("This is deco3") def wrap3(*args, **kwargs): print('enter deco3') result = func(*args, **kwargs) print('exit deco3') return result return wrap3 @deco1 @deco2 @deco3 def foo2(x, y): return x ** y print('_' * 100) foo2(3, 4)
This is deco3 This is deco2 This is deco1 ____________________________________________________________________________________________________ enter deco1: enter deco2 enter deco3 exit deco3 exit deco2 exit deco1
上面的代码做的是一个三层的装饰的,首先Python还是根据语法糖,分别执行了三个装饰器,所以先输出了三个装饰函数里面的打印输出。
从初始化来看,装饰器初始化是从下到上的。再来分析一下,装饰器在执行装饰器函数的时候,做了什么赋值。
初始化过程中,首先foo2被装饰
foo2 = deco3(foo2)的返回函数wrap3
接下来:wrap3的函数继续传递给deco2
foo2 = deco2(wrap3)返回函数wrap2
再接下来:wrap2的函数继续传递给deco1
foo2 = deco1(wrap2)返回函数wrap1
所以初始化的最后,foo2变成了wrap1
所以在执行foo2()的过程,就变成了先执行wrap1,执行wrap1就要执行wrap1里面的func为wrap2,执行wrap2就是执行wrap2里面的wrap3,最后wrap3执行它里面的foo2函数。
但foo2函数执行后,从wrap3开始执行返回,通过result传递,最后传递到wrap1的result,为了执行foo,其实装饰器做了很多,感觉很绕。
很多框架的多层装饰器也差不多,为了判断你的函数能否执行,前面加了很多装饰器来帮助过滤参数,当你执行函数以后,也可以通过向外传递的过程中,设置一些条件,来限制最终的函数执行结果能不能传递出去。其实这也是我为什么现在不急着上手Django,Flask的原因,希望学好了基础,再去学那些框架,不仅知道如何用,更加知道为什么这么用,这样我觉的学起来会轻松很多。
再来一个带参数的装饰器,带参数的装饰器本质就是一个装饰器的生产机器,就是再装饰器的外面又套了一层函数,只不过返回了是这个装饰器。
def sum_rand(n): def lll(func): def wrap(*args, **kwargs): c = 0 for i in range(n): c += func(*args, **kwargs) return c return wrap return lll @sum_rand(90000) # 执行了生成一个lll的装饰器 def fxx(x, y): return x * y print(fxx(2, 3))
上面是一个带参数的装饰器,返回的是一个两数乘积累加多次的和。
@sum_rand(90000)就是一个装饰器的生产器
其实@sum_rand(90000)就是lll装饰器函数,只不过为什么需要读取他的参数,套在装饰器的外层,然后让它返回一个装饰器。
写到这里函数的装饰器差不多就这些,回头来看,其实也就那么一回事,下一个章节,我会记录一下类的装饰器。
原文地址:https://www.cnblogs.com/sidianok/p/11943705.html
- 高颜值可定制在线绘图工具-第三版
- network3D 交互式网络生成
- 如何用六点教会老婆写 Python ?
- 连高晓松都想学的区块链江湖切口,「HODL」是什么意思?
- Spring Data REST 与 Spring RestTemplate 实战详解
- 程序员炒股,如何计算股票投资组合的风险和收益
- Docker 容器化部署运维 OpenStack 和 Ceph
- 关于设计模式的思考
- Spring 框架之 AOP 原理剖析
- Java 平台反应式编程(Reactive Programming)入门
- 从原理到实例,他用区块链技术做一了个COIN 客户端
- 解锁Spring Data Redis的正确使用姿势
- 互联网厂工必知必会:SQL基础篇
- 算法入门,其实可以像读小说一样有趣
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- C++ 万字长文第一篇---拿下字节面试
- 家国梦自动收取金币、货物、升级建筑、拆相册等脚本
- matplotlib 设置移动边框
- 发布你的第一个nodejs c++插件
- nodejs多线程的探索和实践
- 3分钟短文 | Laravel 检验关联模型是否存在的2个必知必会方法
- python读取ini配置的类封装
- ESP32蓝牙的Gatt Client的例子演练
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
- 3分钟短文 | Laravel 内3种数据校验的写法,你喜欢哪一个?
- 纯JavaScript实现的MQTT智能门锁
- 3分钟短文 | Laravel 灵活地获取当前请求的路由地址
- 云原生安全 | docker容器逃逸
- 字节数组X中存放着 0~F共16个十六进制数,请将这些数以十六进制形式显示在屏幕上。
- ubuntu 16安装asp.net