前面章节中,我们已经讲解了 Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod()、classmethod() 和 property() 都是 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

函数装饰器可以嵌套

上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:

@funA

@funB

@funC

def fun():

#...

上面程序的执行顺序是里到外,所以它等效于下面这行代码:

fun = funA( funB ( funC (fun) ) )

这里不再给出具体实例,有兴趣的读者可自行编写程序进行测试。

python装饰器-Python @函数装饰器及用法(超级详细)相关推荐

  1. python function at 0x00000_Python函数装饰器原理与用法详解

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  2. Python pandas 中loc函数的意思及用法,及跟iloc的区别

    Python pandas 中loc函数的意思及用法,及跟iloc的区别 loc和iloc的意思 loc和iloc的区别及用法展示 参考文献 loc和iloc的意思 首先,loc是location的意 ...

  3. Python语言学习:python语言的特点、入门、基础用法之详细攻略

    Python语言学习:python语言的特点.入门.基础用法之详细攻略 相关内容 Python 基础教程 目录 python语言的特点 python语言的入门 python语言的基础用法 python ...

  4. python类方法调用装饰_Python3 @classmethod 函数装饰器 声明一个类方法

    Python3 @classmethod 函数装饰器 声明一个类方法 @classmethod函数装饰器的主要作用是将一个类的普通方法(需要实例化使用)声明为一个类方法(可以直接使用类名调用).在类的 ...

  5. Python的神奇功能——函数装饰器MetaClass

    Python中的装饰器,会让很多人望而却步.不要被它吓跑,啃下它,其实超有用,也没有想象中难. 所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改. Python的装饰 ...

  6. python 函数装饰器_python函数装饰器的用法

    函数装饰器: 开闭原则:在面向对象编程领域中,开闭原则规定"软件中的对象(类,模块,函数等等)应该对于扩展是开放的, 但是对于修改是封闭的",这意味着一个实体是允许在不改变它的源代 ...

  7. python函数修饰器_Python函数装饰器指南

    Python 具有强大的功能和富有表现力的语法.我最喜欢的装饰之一.在设计模式的上下文中,装饰器动态更改方法或类功能,而不必直接使用子类.当您需要扩展功能,但不想修改原函数时,这是理想的选择.我们可以 ...

  8. python基础教程:函数装饰器详解

    谁可以作为装饰器(可以将谁编写成装饰器): 函数 方法 实现了__call__的可调用类 装饰器可以去装饰谁(谁可以被装饰): 函数 方法 类 基础:函数装饰器的表现方式 假如你已经定义了一个函数fu ...

  9. Python小白学习之函数装饰器

    装饰器 2018-10-25 13:49:37 装饰器从字面意思就是用来装饰的,在函数可以理解为:在函数中,我们不想影响原来的函数功能,又想给函数添加新的功能,这时候我们就用到了装饰器. 一般函数操作 ...

  10. python 类装饰器和函数装饰器区别_python进阶之装饰器之4在类中定义装饰器,将装饰器定义为类,两者的区别与联系...

    # 把装饰器定义为类 # 定义中需要实现__call__(),__get__() 方法 import types from functools import wraps class Profiled: ...

最新文章

  1. Javascript 创建书签小工具 (bilibili视频下载为例)
  2. angularjs组件间通讯_angular组件间通讯的实现方法示例
  3. vue设置多选框默认勾选_vue中复选框怎么默认全选,至少选择4个才可以点击下一步...
  4. php null空值(转)
  5. Android Studio 创建/打开项目时一直处于Building“project name”Gradle project info 的解决...
  6. OpenDDS的学习和使用
  7. cad安装日志文件发生错误_Cad2012安装总是到60%左右失败,现贴出安装出错的安装日志文件,望大神帮忙解决下...
  8. 惠普、华三、华为、戴尔、联想服务器维保查询地址汇总
  9. java Math.random()
  10. SpringCloud-Learning -作者:翟永超
  11. Python操作Redis之mset和mget
  12. 【C++】pcl中的简单点云可视化
  13. 图片报道:2008年12月4日夜,暴风雪突袭烟台(上)
  14. Matlab如何删除矩阵中的零元素,重新整理不等行的矩阵
  15. 深入理解计算机系统(v3) 第三章、程序的机器级表示(Linux)
  16. Apache PHP 的gzip压缩输出的实现方法
  17. 服务器定时开机设置方法
  18. v-show和v-if的区别 display:none和visible: hidden的区别
  19. php 里的cl框架手册,CI框架学习笔记(一)
  20. 注册会计师考试让用计算机,注册会计师考试让不让带计算器?能带什么样的?...

热门文章

  1. 【转】每天一个linux命令(38):cal 命令
  2. 2011年11月份第一周51Aspx源码发布详情
  3. 动手动脑问题2加课堂练习
  4. apcloud混合式开发app学习笔记
  5. php设定错误和异常处理可使用的函数
  6. Matrix 矩阵
  7. SQL Server 表分区注意事项(转载)
  8. 程序员面对下列技术问题,如何做决策
  9. 财务学python有意义吗-8年老财务:财务分析学python就能提高效率?一半人是骗子...
  10. python爬虫实战-python爬虫实战一:分析豆瓣中最新电影的影评