Python @函数装饰器及用法(超级详细)
函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:
#funA 作为装饰器函数
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
@funA
def funB():
#...
实际上,上面程序完全等价于下面的程序:
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
def funB():
#...
funB = funA(funB)
通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:
- 将 B 作为参数传给 A() 函数;
- 将 A() 函数执行完成的返回值反馈回 B。
举个实例:
#funA 作为装饰器函数
def funA(fn):
print("C语言中文网")
fn() # 执行传入的fn参数
print("http://c.biancheng.net")
return "装饰器函数的返回值"
@funA
def funB():
print("学习 Python")
程序执行流程为:
C语言中文网
学习 Python
http://c.biancheng.net
在此基础上,如果在程序末尾添加如下语句:
print(funB)
其输出结果为:
装饰器函数的返回值
显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。
实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。
带参数的函数装饰器
在分析 funA() 函数装饰器和 funB() 函数的关系时,细心的读者可能会发现一个问题,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那应该如何传值呢?
比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:
def funA(fn):
# 定义一个嵌套函数
def say(arc):
print("Python教程:",arc)
return say
@funA
def funB(arc):
print("funB():", a)
funB("http://c.biancheng.net/python")
程序执行结果为:
Python教程: http://c.biancheng.net/python
这里有必要给读者分析一下这个程序,其实,它和如下程序是等价的:
def funA(fn):
# 定义一个嵌套函数
def say(arc):
print("Python教程:",arc)
return say
def funB(arc):
print("funB():", a)
funB = funA(funB)
funB("http://c.biancheng.net/python")
如果运行此程序会发现,它的输出结果和上面程序相同。
显然,通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。
但还有一个问题需要解决,即如果当前程序中,有多个(≥ 2)函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等,怎么办呢?
最简单的解决方式是用*args 和 **kwargs 作为装饰器内部嵌套函数的参数,*args 和 **kwargs 表示接受任意数量和类型的参数。举个例子:
def funA(fn):
# 定义一个嵌套函数
def say(*args,**kwargs):
fn(*args,**kwargs)
return say
@funA
def funB(arc):
print("C语言中文网:",arc)
@funA
def other_funB(name,arc):
print(name,arc)
funB("http://c.biancheng.net")
other_funB("Python教程:","http://c.biancheng.net/python")
运行结果为:
C语言中文网: http://c.biancheng.net
Python教程: http://c.biancheng.net/python
*args的使用方法
*args 用来将参数打包成tuple给函数体调用
例子一:
def function(*args):
print(args, type(args))
function(1)
输出结果以元组的形式展示:

例子二:
def function(x, y, *args):
print(x, y, args)
function(1, 2, 3, 4, 5)
输出结果:

**kwargs的使用方法
**kwargs 打包关键字参数成dict给函数体调用
例子一:
def function(**kwargs):
print( kwargs, type(kwargs))
function(a=2)
输出结果以列表形式展示:

例子二:
def function(**kwargs):
print(kwargs)
function(a=1, b=2, c=3)
输出结果:

注意点:参数arg、*args、**kwargs三个参数的位置必须是一定的。必须是(arg,*args,**kwargs)这个顺序,否则程序会报错。
def function(arg,*args,**kwargs):
print(arg,args,kwargs)
function(6,7,8,9,a=1, b=2, c=3)
输出结果:

函数装饰器可以嵌套
上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:
@funA
@funB
@funC
def fun():
#...
上面程序的执行顺序是里到外,所以它等效于下面这行代码:
fun = funA( funB ( funC (fun) ) )
这里不再给出具体实例,有兴趣的读者可自行编写程序进行测试。
Python @函数装饰器及用法(超级详细)的更多相关文章
- Python函数装饰器高级用法
在了解了Python函数装饰器基础知识和闭包之后,开始正式学习函数装饰器. 典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过 ...
- Python @函数装饰器及用法
1.函数装饰器的工作原理 函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: #funA 作为装饰器函数 def funA(fn): #... fn ...
- Python函数装饰器原理与用法详解《摘》
本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...
- python函数-装饰器
python函数-装饰器 1.装饰器的原则--开放封闭原则 开放:对于添加新功能是开放的 封闭:对于修改原功能是封闭的 2.装饰器的作用 在不更改原函数调用方式的前提下对原函数添加新功能 3.装饰器的 ...
- Python的装饰器实例用法小结
这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...
- Python 函数装饰器
首次接触到装饰器的概念,太菜啦! Python 装饰器可以大大节省代码的编写量,提升代码的重复使用率.函数装饰器其本质也是一个函数,我们可以把它理解为函数中定义了一个子函数. 例如我们有这么一个需求, ...
- Python高手之路【四】python函数装饰器
def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...
- python 函数 装饰器 内置函数
函数 装饰器 内置函数 一.命名空间和作用域 二.装饰器 1.无参数 2.函数有参数 3.函数动态参数 4.装饰器参数 三.内置函数 salaries={ 'egon':3000, 'alex':10 ...
- Python 函数装饰器简明教程
定义类的静态方法时,就使用了装饰器.其实面向对象中的静态方法都是使用了装饰器. @staticmethod def jump(): print(" 3 meters high") ...
随机推荐
- Jmeter接口数据流测试及持续集成部署:(一)Jmeter环境搭建:安装JDK、安装Jmeter、安装Fiddler、安装ant
Jmeter环境搭建 1.安装JDK 官方下载地址:https://www.oracle.com/java/technologies/downloads/ 安装方法:双击jdk安装包,一直下一步安装即 ...
- 04 | 函数扩展 | es6
函数参数的默认值 基本用法 ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; console.log(x ...
- Linux常见目录结构
目录 描述 /home 包含Linux系统上各用户的主目录,子目录默认以该用户名命名 /etc 包含Linux系统上大部分的配置文件,建议修改配置文件之前先备份 /var 该目录存放不经常变化的数据, ...
- 如何设计一个高内聚低耦合的模块——MegEngine 中自定义 Op 系统的实践经验
作者:褚超群 | 旷视科技 MegEngine 架构师 背景介绍 在算法研究的过程中,算法同学们可能经常会尝试定义各种新的神经网络层(neural network layer),比如 Layer No ...
- 【华为昇腾】 序言:从昇腾AI软硬件平台聊起
2021年是很值得纪念的一年,从上半年开始跟随导师编写有关华为昇腾软件栈CANN的教材,一年的时间反复迭代 终于快要出版了. 这一系列博客可以视作我从编者的角度,重新梳理的全书思路.明年入职商汤之后要 ...
- java 代理模式实现代码
目录 1.静态代理 2.动态代理 1.静态代理 接口类AdminService.java接口 public interface AdminService { void update(); Object ...
- CF1368F Lamps on a Circle
思考我们一定有最后一个状态是空着的灯是按照一个间隔\(k\) 只要将原来\(n\)个灯,每\(k\)个分一组,强制将最后一盏灯不选,并且第n盏灯不选,需要注意的是某一组一定会被第二个人全部关掉,那么可 ...
- Codeforces 1464F - My Beautiful Madness(树的直径)
Codeforces 题面传送门 & 洛谷题面传送门 树上数据结构大杂烩(?) 首先考虑什么样的点能够在所有路径的 \(d\) 邻居的交集内.显然如果一个点在一条路径的 \(d\) 邻居内则必 ...
- 洛谷 P7154 - [USACO20DEC] Sleeping Cows P(dp)
Portal 题意: 给出两个序列 \(a_1,a_2,\dots,a_n\),\(b_1,b_2,\dots,b_n\),\(i\) 与 \(j\) 能匹配当且仅当 \(a_i\leq b_j\). ...
- tcp可靠传输的机制有哪些(面试必看
一.综述 1.确认和重传:接收方收到报文就会确认,发送方发送一段时间后没有收到确认就重传. 2.数据校验 3.数据合理分片和排序: UDP:IP数据报大于1500字节,大于MTU.这个时候发送方IP层 ...