我们要完全理解python装饰器,不是很容易,主要归结有如下困难:

1. 关于函数“变量”(或“变量”函数)的理解

2. 关于高阶函数的理解

3. 关于嵌套函数的理解

放心,我会用浅显的例子来说明其中的知识。

1. 首先有这样一个例子

首先我们用pycharm创建一个工程,这里我创建的工程名为:py3test,这个工程名自己取,当然你也可以不使用pycharm这款IDE,直接用notepad++这样的文本编辑器也是可以的。

然后我们分别在工程目录先创建三个文件:

__inti__.py

s.py

test.py

如下图所示:

图一

接下来在s.py文件中添加内容:

def f1():

print('f1')

def f2():

print('f2')

def f100():

print('f3')

在test.py文件中添加内容:

import s

s.f1()

s.f2()

s.f100()

__init__.py 文件不用添加内容,有人可能会想不添加内容,那这个文件有什么用呢?下面我找了几个资料可以作为参考:

https://juejin.im/post/5a2cfc4f6fb9a044ff316588

https://segmentfault.com/q/1010000012365228

运行test.py文件

运行test.py文件后可以看到。在test.py中成功的调用了s.py中的函数。

接下来修改s.py文件

在s.py文件中的每个函数前都添加

print('装饰器')

如图所示

图二

然后重新运行test.py文件,结果如图所示:

图三

接着我们这想这样的一个问题,在我们最开始学习编程的时候,如果有个题目要求我们输出100个'hello world!',我们自然的想到了,直接print('hello world!')然后写100行,自然也就打印出了100个hello world!,在我们学习了循环之后就知道了直接用一个for循环100次就行了。

同样的在我们学习函数之前,如果你写的程序中会多次引用一段相同的代码,或者是要多次执行相同的功能,然后我们就用函数把这些需要多次调用的功能封装起来,就变成了函数,在面对对象编程中也叫方法。

如果现在有这么一个需求就是在现有的100个函数执行前加上print('hello world!'),那么我们就在学习python的装饰器后就不用批量的在每个函数前面添加代码了,而是使用python的装饰器。

例子

把s.py文件修改问如下所示:

def outer(func):

def inner():

print('hello world!')

return func()

return inner

@outer

def f1():

print('装饰器')

print('f1')

@outer

def f2():

print('装饰器')

print('f2')

@outer

def f100():

print('装饰器')

print('f3')

然后执行test.py文件,输出结果如下:

hello world!

装饰器

f1

hello world!

装饰器

f2

hello world!

装饰器

f3

分析:相比之前的代码,在test.py文件中调用f1,f2f,f3函数的时除了打印原本的两条语句之前还另外在执行两条语句之前还执行了,print('hello world!')语句。这是为什么呢?这是什么原理呢?

首先我们要清楚,在python中函数是可以赋值给其他变量的,也就是和c语言中的函数指针差不多,一个函数名本身就是值的这个函数的地址,既然是地址那么就是可以传递的。如果我们有了函数的地址,那么也就调用这个函数了。

看下面的例子:

>>> def fun1(): # 定义函数fun1

... print('fun1')

...

>>> fun1() # 调用函数fun1

fun1 # 调用函数fun1 输出 "fun1’

>>> fun2 # 然后输出看一下 fun2 变量

Traceback (most recent call last):

File "", line 1, in

NameError: name 'fun2' is not defined # 输出fun2时候 报错 没有定义

>>> fun1 # 输出查看fun1 变量为:0x10a7fee18

>>> fun2 = fun1 # 把fun1 变量的值赋值给 fun2

>>> fun2 # 输出fun2 发现和fun1的值一模一样

>>> fun2() # 把fun2当作函数调用尽然功能和fun1一模一样

fun1

从以上的例子可以看出来函数名可以通过赋值操作把地址赋值给其他变量并用通过其他变量也可以调用。把这个例子再升级一下:

>>> def fun3(f): # 定义一个函数,fun3

... print(f) # 输出参数f

... f() # 特别注意这里,在前面说到了一个变量是可以被赋值成一个函数

... #的地址的,这里的f就是被看成了被赋值成函数地址了,然后通过f被赋值的地址调用那个函数

>>> fun3(fun1) # 这里在调用fun3的时候把fun1函数的地址传递给fun3的参数

# 可知输出f变量的内容正式上个例子中fun1函数的地址值

fun1

注意这个代码是接这上面一个例子的

然后我们再升级:

>>> def fun4(f): #照常我们定义一个普通的函数,参数是f

... def fun5_in_fun4(): #在函数fun4内又定义一个函数fun5_in_fun4

... print('fun5_in_fun4') # 函数fun5_in_fun4体内的语句输出'fun5_in_fun4'字符串

... f() # 调用传入的函数f 其实在下面传入的还是fun1

... return fun5_in_fun4 #特别注意这里,这里我们把在fun4内定义的函数通过返回值返回

...

>>> fun = fun4(fun1) # 调用fun4传入fun1,这里用fun来接受fun4的返回值是'fun5_in_fun4'函数地址

fun1 # 在fun4还输内调用了传入的函数fun1 输出 'fun1'字符串

>>> fun() # 在上一句代码中fun接收了fun4的返回值是一个函数的地址,那么就可以调用fun4内部定义的函数了

fun5_in_fun4 # 调用fun4内部的函数输出'fun5_in_fun4'字符串。

接着我们再改造一下函数fun4

>>> def outer(f):

... def inner():

... f()

... print("fun5_in_fun4")

... return 'function in fun4'

... return inner

...

>>> s = outer(fun1)

>>> string = s()

fun1

fun5_in_fun4

>>> string # string 得到的返回值便是执行函数 inner 后返回的字符串

'function in fun4'

通过这样一改造是不是就是我们最开始例子中的python构造器了

我们最开始在定义函数outer的时候,估摸着会传入一个函数,然后在outer函数内部重新定义了一个新的函数inner,在inner函数内部不仅调用了传入的函数,更是增加了新的代码(print("fun5_in_fun4")),并且还有新的返回值(return 'function in fun4')。

更重要的是在outer函数中把内部函数inner通过返回值的方式返回

我们在调用函数outer的时候也就通过变量s接受了outer的返回值,接下里调用s,并接受返回值。

通过这样一个过程我们便熟悉了python装饰器的原理,这里我们便可以重新倒回去看,文章最开始的例子了。

我们在执行文件test.py,调用s.f1(),这里调用的f1,已经不是前的f1了,从以上的分析中我们可以得知,其实这里调用的就是outer函数的内部函数inner了。

至于为什么调用的函数为什么不是原来的f1 而是内部函数inner那这就要关系到@outer了,@outer的功能也就是把f1函数替换为内部函数inner

接下来我们详细说一说@outer的具体功能:

首先@outer的用法是:用符号@+函数名并放在一个函数定义的前面

@outer有两个主要的功能:

自动的执行outer函数,并把下面的函数f1当作参数传递给函数outer

修改文件s.py如下:

def outer(func):

print('before')

print(func)

def inner():

print('hello world!')

return func()

return inner

@outer

def f1():

print('f1')

然后运行文件s.py然后看到输出了字符串before,

将outer函数的返回值重新赋值给f1

重新修改文件s.py如下所示:

def outer(func):

return 'return' # 把返回值赋值给f1

@outer

def f1():

print('f1')

print(f1)

运行s.py可知,打印出了'return'字符串。

python装饰器的参数传递

看这样一个例子:

修改文件s.py如下所示:

def outer(func):

def inner(): # 没有参数

ret = func() # 没有参数

print('hello')

return ret

return inner

@outer

def f1(a): # 有一个参数 a

print('a:',a)

return 'f1'

修改文件test.py如图所示:

import s

s.f1(1)

执行test.py文件,但是报错了。这是为什么呢?

我们先根据原理分析下,在test.py文件中到我们调用s.py文件中的f1函数时,实际上是调用的函数inner,观察源代码,可以发现inner函数并没有参数,但是在调用的时候却传递给了一个参数,故就报错了,那怎么解决这个问题呢?

如果是缺少参数,那我们在inner函数中加上参数就行了。修改s.py中的代码如下:

def outer(func):

def inner(arg):

ret = func(arg)

print('hello')

return ret

return inner

@outer

def f1(a):

print('a:',a)

return 'f1'

这样我们再运行test.py文件就不会报错了,但是这样还是会有问题!

在s.py文件中增加一个函数f2,如下所示:

def outer(func):

def inner(arg):

ret = func(arg)

print('hello')

return ret

return inner

@outer

def f1(a):

print('a:',a)

return 'f1'

@outer

def f2(a,b,c):

print('a:',a,' b:',b," c:",c)

return 'f1'

接着在test.py中调用f2函数,修改test.py文件如下:

import s

s.f1(1)

s.f2(1,2,3)

然后运行test.py则还是会报一个参数错误,在f1中只有一个参数,而f2中则有三个参数,这应该怎么觉得呢?

这里肯定是不能修改原函数f1和f2的,那就只有修改outer函数了,既然是参数问题,就自然应该是inner函数出了问题,这里就应该是修改inner函数的参数了,这里就应该用python中的万能参数了。

修改s.py 文件如下所示:

def outer(func):

def inner(*args, **kwargs): # 使用python的万能参数

ret = func(*args, **kwargs)

print('hello')

return ret

return inner

@outer

def f1(a):

print('a:',a)

return 'f1'

@outer

def f2(a,b,c):

print('a:',a,' b:',b," c:",c)

return 'f1'

python装饰器的应用范围

从以上讲解我们明白了装饰器的执行原理,装饰器的功能是修饰一个函数,也就是当我们休要在某个函数体运行之前或者是运行之后添加某个功能,而不用修改原函数,那我们就可以使用python的装饰器。

python装饰器原理-深刻理解python装饰器相关推荐

  1. python装饰器原理-深入理解 Python 装饰器

    作者简介 曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器.漏洞管理平台.安全 SaaS 平台等. Python 是一门追求优雅编程的语言, ...

  2. python list 实现原理,彻底理解Python list切片原理

    关于list的insert函数 list#insert(ind,value)在ind元素前面插入value 首先对ind进行预处理:如果ind<0,则ind+=len(a),这样一来ind就变成 ...

  3. Python程序员必须深刻理解的几个Warning

    Python程序员必须深刻理解的几个Warning 在Python编程中,很多时候我们会看到一些Warning输出,这些信息通常是针对我们的代码中存在的问题或潜在的风险.但是有时候我们并不希望看到这些 ...

  4. HLSL着色器原理:(一)着色器基础

    小光!小光!小光!小光!小光! 本文所总结视频为或许是小光从油管搬运到B站的视频:传送门 本篇主要汇总HLSL着色器的知识原理部分,并涉及少量必要的代码知识点,主要为知识点总结,实践部分建议参照其他S ...

  5. 什么是python装饰器_深入理解 Python 装饰器

    作者简介 曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器.漏洞管理平台.安全 SaaS 平台等. Python 是一门追求优雅编程的语言, ...

  6. python装饰器作用-如何理解Python装饰器?

    晚上失眠,怒上知乎答题! 刚好最近我的python专栏里写过一篇装饰器相关的,不说废话,直接上干货! /> 目录如下:1.装饰器是什么? 2.如何使用装饰器? 3.内置装饰器 一.装饰器是什么? ...

  7. python装饰器原理-简单了解python装饰器原理及使用方法

    这篇文章主要介绍了简单了解python装饰器原理及使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 如果你接触 Python 有一段时间了的话 ...

  8. python装饰器的通俗理解_python装饰器的通俗理解

    在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过思考和查阅才能略有领悟,我希望以下的内容会对你有帮助,我也努力通过通俗的方式使得对Py ...

  9. python with关键字_完全理解Python关键字with与上下文管理器

    如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 "with" 关键字的语句,它通常用在什么场景呢?今天就来说说 with 和 上下文管理器. 对于系统资源如文件.数据 ...

最新文章

  1. 强势 图解 AC自动机(保证您一次就能学会!)
  2. 通过setTimeout处理click,dblclick,mousedown等事件的冲突
  3. python:array和list转换以及数组切片
  4. cheerio api
  5. [bzoj4003][JLOI2015]城池攻占_左偏树
  6. 阿里云CDN操控2.0版本正式发布
  7. spring设置全局异常处理器
  8. 差距缩小 WLAN市场销量逼近有线网络
  9. python少儿编程教案_超好玩的Python少儿编程
  10. java8实战:使用流收集数据之toList、joining、groupBy(多字段分组)
  11. 智慧城管三维可视化决策系统平台(数字孪生)—解决方案开发案例
  12. 初级软考程序员科目一考什么内容?
  13. Quartz定时任务框架(一)
  14. SWUST OJ【972】
  15. git、githut、码云概念和使用,md文件编辑,
  16. 吴恩达机器学习python实现8 异常检测及推荐系统
  17. 20220727使用汇承科技的蓝牙模块HC-05配对手机进行蓝牙串口的演示
  18. 抖音账号如何做好私域流量,私域流量是什么
  19. 2020-05-13
  20. 要想文章好,图片少不了,22个技能助您获得美图(带字幕视频)

热门文章

  1. 0428(字典,列表,循环)
  2. 2017-2018-1 20155202 《信息安全系统设计基础》第10周学习总结
  3. Rsync文件同步服务
  4. php 序列化储存和转化 json_encode() json_decode($q,true)
  5. Educational Codeforces Round 2 B. Queries about less or equal elements
  6. log file switch (checkpoint incomplete)
  7. magento 加速(.htaccess)
  8. HDU 1181 变形课
  9. 为了今年印象最深刻的唱片,转演唱会消息一个,虽然我去不了 55555
  10. mac下安装android-sdk