通俗易懂了解Python装饰器原理
作者:mskitten 发布时间:2021-07-18 13:48:20
作用
装饰器可以用于用于装饰一个函数或方法,使得在不修改原函数、方法代码的前提下,为方法添加前置或后置操作;
例如突然想要计算一下各个函数的执行时间,又不希望在每一个函数中添加tim.time()来计算执行时间
用法
装饰器的写法网上很多,但是我觉得还是尽量先理解,再知道怎么写会比较好,所以会先说如何理解,在后面重写用法
实现
了解装饰器是如何实现的,远比会写装饰器更重要,简单的说装饰器就是接收一个函数对象,然后先执行前置操作,再执行函数,再执行后置操作;
这么说可能有些抽象,或者举一个不那么恰当的比较贴近生活的例子;
假设你有一台像这样的小风扇:
这台风扇可以充电,有一个开关,打开之后扇叶会旋转,开始工作,当然你也可以插着电打开开关,也可以充好电之后带走,在其他地方打开开关
如果把这个风扇置于一切皆是对象的Python中,风扇就是一个对象,他实现的功能就是出风:
def fengshan():
return '出风'
为了更好和例子结合,我们用pinyin命名
现在我们实现了一个fengshan函数,返回吹风
如果你稍微有点基础,你就能知道如何调用这个方法
def fengshan():
return '出风'
print(fengshan())
不要觉得这很基础很墨迹,如果需要理解装饰器,你必须知道,调用函数的方式是函数名称加上括号fengshan()
而这个基础中的基础中的括号()就是执行函数的开关,如果我们不加括号
def fengshan():
return '出风'
print(fengshan)
返回的将是一个函数对象(例子中的风扇本身)
<function fengshan at 0x7f8e7c4a6950>
这里的意思是 一个叫fengshan的funciont,地址在0x7f8e7c4a6950
那现在我们就可以把风扇带走,在其他地方使用
def fengshan():
return '出风'
func = fengshan
print(fengshan)
print(func)
返回
<function fengshan at 0x7f570eaf3950>
<function fengshan at 0x7f570eaf3950>
这说明func和fengshan是等价的,他们在同一块内存中,所以当我们执行func() 也等价于执行fengshan
def fengshan():
return '出风'
func = fengshan
print('下面是执行fengshan')
print(fengshan())
print('下面是执行func')
print(func())
返回
下面是执行fengshan
出风
下面是执行func
出风
理解到这里之后你也就能理解装饰器的实现了,让我们再看一个例子
def fengshan():
return '出风'
def wrapper(func):
return func
print(fengshan)
print(wrapper(fengshan))
这个例子中我们除了保留刚刚一直在用的fengshan函数之外,又定义了一个wrapper
因为python中一切皆是对象,函数也是对象,而函数的入参也可以接收对象,所以函数对象可以作为参数传递给另一个函数wrapper
这个wrapper中什么都没有做,只是返回了接收的func对象,我们打印出来两个对象,可以发现他们其实是同一个对象
<function fengshan at 0x7f9b0c92f950>
<function fengshan at 0x7f9b0c92f950>
现在你就已经理解了装饰器的实现了,而且如果你跟着文中的代码敲一遍,你就已经写了一个装饰器,你只需要稍加修改,比方说,我们在wrapper内部执行接收的func,并且,在前后加上一些操作
def wrapper(func):
print('在wrapper中执行func前')
print(func())
print('在wrapper中执行func后')
wrapper(fengshan)
返回
在wrapper中执行func前
出风
在wrapper中执行func后
如果你觉得很神奇,无法理解,可以回到风扇的图片重新再读一遍,当你理解了上述的代码之后,我们就可以加快速度,完成真正的装饰器的编写
用法
当你成功明白了函数被定义和调用的过程之后,我们开始考虑实际场景,上一段代码中我们还是改变了原有函数的调用
从调用fengshan变成了调用func(fengshan)
我们想要实现不改变原有代码和调用方式的情况下为原有函数添加前置或后置操作,就需要再优化一下我们的装饰器
def fengshan():
print('出风')
def wrapper(func):
def f():
print('在wrapper中执行func前')
func()
print('在wrapper中执行func后')
return f
fengshan = wrapper(fengshan)
fengshan()
为了看到执行过程,我们把fengshan内部的return改为print
返回
在wrapper中执行func前
出风
在wrapper中执行func后
这里我们的改变是在wrapper原有的实现中又包了一层方法f,再回想一下我们前面风扇的例子,现在当我们执行wrapper的时候,执行了什么?
wrapper(func)的返回,是一个函数对象f,这个函数对象的开关没有被打开,f中的代码不会被执行
我们又用同名的fengshan对象去接收了这个函数对象f,在最后一行打开fengshan的开关 -- fengshan(),这时候函数对象f中的代码,才刚被执行
如果看不懂的话,建议从风扇图片开始再看一遍,如果你看懂了,建议你也再看一遍,至此,我们就已经完成了一个装饰器,为了更方便使用装饰器,Python给我们提供了更简便的方法
def wrapper(func):
def f():
print('在wrapper中执行func前')
func()
print('在wrapper中执行func后')
return f
@wrapper
def fengshan():
print('出风')
fengshan()
返回
在wrapper中执行func前
出风
在wrapper中执行func后
补充知识
如果你已经理解了装饰器的执行逻辑,你也就会知道如何让装饰器支持带参数的方法,这也是我们写装饰器的常规操作
def wrapper(func):
def f(*args, **kwargs):
print('在wrapper中执行func前')
func(*args, **kwargs)
print('在wrapper中执行func后')
return f
@wrapper
def fengshan(str_obj):
print(str_obj)
fengshan(str_obj='出风')
来源:https://www.cnblogs.com/lanheader/p/13558738.html


猜你喜欢
- 本文实例讲述了python简单获取本机计算机名和IP地址的方法。分享给大家供大家参考。具体实现方法如下:方法一:>>> i
- 前言结构体是包含多个字段的集合类型,用于将数据组合为记录。这样可以将与同一实体相关联的数据利落地封装到一个轻量的类型定义中,然后通过对该结构
- 前言:任何一个编程者都少不了要去调试代码,不管你是高手还是菜鸟,调试程序都是一项必不可少的工作。一般来说调试程序是在编写代码之后或测试期修改
- 这篇文章主要介绍了Python csv文件的读写操作实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 一、auth模块# 创建超级用户python manage.py createsuperuserfrom django.contrib im
- Mysql安装、配置、优化,供大家参考,具体内容如下Mysql下载首先登入官网下载mysql的安装包,官网地址https://dev.mys
- Confusion Matrix在机器学习领域,混淆矩阵(confusion matrix),又称为可能性表格或是错误矩阵。它是一种特定的矩
- 完成asp语言对XML文档中指定节点文本的增加、删除、修改、查看 <% '-------------------
- 前言GO语言在WEB开发领域中的使用越来越广泛,Hired 发布的《2019 软件工程师状态》报告中指出,具有 Go 经验的候选人是迄今为止
- 如下所示:def is_chinese(uchar): """判断一个unicode是否是汉字"&q
- 本文实例讲述了Python单元测试与测试用例。分享给大家供大家参考,具体如下:单元测试与测试用例简介测试用例是一组单元测试,这些单元测试一起
- 堆 heap 值类型 原始类型(primitive type) Undefined: undefined (注意大小写:类型/ 值,下同)
- 借助 GitHub 的网络钩子webhook,开发者可以创建很多有用的服务。从触发一个 Jenkins 实例上的 CI(持续集成) 任务到配
- Django 简介Django是一个开放源代码的Web应用框架,由Python写成。采用了MTV的框架模式,即模型M,视图V和模版T。它最初
- 许可和分发权限Access 2003 Developer Extensions随附的许可协议简化了解决方案或代码段的分发过程。这些协议包括免
- 一、排序排序查询语法:select * from 表名 order by 列1 asc/desc [,列2 asc/desc,..
- 简单的显示记录已经掌握,现在需要的就是通过ASP将信息内容插入到数据库中。一、拥有数据库cnbruce.mdb本数据库的作用就是用来 * 入数
- 1.什么是MD5加密MD5消息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出
- 1.当前时间戳转换为指定格式的日期# -*- coding: utf-8 -*-# @Time : 2019/5/31 10:5
- 通常来说,Python的变量/数据类型非常多,但是它是不需要用户指定的,因为有些是根据部份系统函数生成,另外一些是自动根据变量的值识别的,这