python中的闭包与装饰器
#原创,转载请留言联系
装饰器的本质就是闭包,所以想知道装饰器是什么,首先要理解一下什么是闭包。
- 闭包
1. 外部函数返回内部函数的引用。
2. 内部函数使用外部函数的变量或者参数。
def outer(outer_num):
def inner(inner_num):
a = outer_num+inner_num
print(a)
return inner f1 = outer(1)
f1(2) 输出:
3
1.outer函数返回inner函数的引用,f1=outer(1),实质就是f1=inner,而且还有,它会传入一个变量outer_num=1。这个变量是outer函数的局部变量,但是他是inner函数的“全局变量”。所以这个变量在执行完f1=outer(1)后,并不会被回收。(我们都知道普通函数里面的变量在运行完函数后会被回收。全局变量不会。)
2.inner函数使用了outer函数的变量。
所以上面那个是一个闭包!
拓展:如果要在内部函数修改外部函数的变量,不是用global,而是用nonlocal。
def outer(num):
def inner():
nonlocal num
num += 1
print(num)
return inner f1 = outer(1)
f1()
f1()
f1() 输出:
2
3
4
说了这么多,闭包有什么用处呢?
当你做一个项目时,当已经实现功能的代码不允许修改了!但是又想添加新功能时,这时候闭包的作用就出来了!
假设有一个这样的基础函数:
def transer():
"""实现转账的函数"""
print("正在转账...")
这是一个实现转账功能的函数,但是还没有身份验证等步骤,我们需要完善这个项目。但是又不能修改这个函数的源代码,这时候应该怎么办呢?没错,就是闭包。
def transer():
"""实现转账的函数"""
print("正在转账...") def outer(func):
def vifi():
"""实现身份验证的函数"""
print("正在验证身份...")
func()
return vifi f1 = outer(transer)
f1() 输出:
正在验证身份...
正在转账...
完美解决!
上面的代码又可以稍微更改成这样:
def outer(func):
def vifi():
"""实现身份验证的函数"""
print("正在验证身份...")
func()
return vifi @outer # 等价于outer(transer)
def transer():
"""实现转账的函数"""
print("正在转账...") transer() 输出:
正在验证身份...
正在转账...
@outer就是装饰器!在不改变函数的定义和调用的前提下,给函数扩展功能,这就是装饰器。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上面装饰器装饰的函数没有接收参数,也没有返回值。当装饰的函数接收情况或者有返回值时怎么操作呢?
1.当被装饰的函数有接收参数没有返回值的情况,装饰器应该怎么改?
def outer(func):
def inner(num): # 这里要写num形参
"""我是装饰的功能"""
func(num) # 这里也要写num形参
return inner @outer
def normal(num):
"""我是被装饰的函数"""
num += 1
print(num) normal(5) 输出:
6
2.当被装饰的函数没有接收参数有返回值的情况,装饰器应该怎么改?
def outer(func):
def inner():
"""我是装饰的功能"""
return func() # 这里要return函数,return的值返回给inner函数,才能接受到。
return inner @outer
def normal():
"""我是被装饰的函数"""
return "我是返回值" result = normal()
print(result) 输出:
我是返回值
3.当被装饰的函数既有接受参数又有返回值的情况,装饰器应该怎么改?
def outer(func):
def inner(num):
"""我是装饰的功能"""
return func(num) # 前面两者综合
return inner @outer
def normal(num):
"""我是被装饰的函数"""
num += 1
print(num)
return "我是返回值" result = normal(5)
print(result) 输出:
6
我是返回值
综合上面各种情况,我们可以写一个万能装饰器,这样的话,就不用根据被装饰函数有没接收参数,有没返回值,而改来改去了。
def outer(func):
def inner(*args,**kwargs):
"""我是装饰的功能"""
result = func(*args,**kwargs)
return result
return inner @outer
def normal(*args,**kwargs):
"""我是被装饰的函数"""
print(args,kwargs)
return 'something' i = normal(5)
print(i) 输出:
(5,) {}
something
这个万能装饰器,无论被装饰函数有没接收参数,有没返回值都可以正常运行了!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
当有多个装饰器装饰一个函数的时候,装饰的顺序是怎样的呢?
def w1(func):
def inner():
return '<b>'+func()+"</b>"
return inner def w2(func):
def inner():
return '<t>' + func() + "</t>"
return inner @w1
@w2
def transer():
return 'hello-world' print(transer()) 输出:
<b><t>hello-world</t></b>
可以看出,虽然执行顺序是从外到里,但是装饰的顺序是从里到外的。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
如果我们有一个需求,希望装饰函数时额外传一个参数flag,当这个参数是1时,就进行装饰。如果是0时,就不进行装饰。应该怎么操作呢?
有一个错误的做法经常很多人犯,如下:
def outer(func):
def vifi(flag):
"""实现身份验证的函数"""
if flag == 1:
print("正在验证身份...")
func()
return vifi @outer
def transer():
"""实现转账的函数"""
print("正在转账...") transer(1)
print("\n")
transer(0) 输出:
正在验证身份...
正在转账... 正在转账...
诶???结果不是正确了吗?不是实现这个功能了吗?但是,装饰器的定义是,在不改变函数的定义和调用的前提下,给函数扩展功能!
这样的写法,不是已经改变了函数的调用了吗?transer函数本来没有形参的,然后你无缘无故给他一个参数......
正确的写法应该是这样!通过装饰器工厂实现对装饰器传递额外的参数!
def outouter(flag):
def outer(func):
def vifi():
"""实现身份验证的函数"""
if flag == 1:
print("正在验证身份...")
func()
return vifi
return outer @outouter(1)
# @outouter(0)
def transer():
"""实现转账的函数"""
print("正在转账...") transer() 输出:
正在验证身份...
正在转账...
正确的做法是在闭包再加一层。用来接受参数。这种做法就叫装饰器工厂!
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
类装饰器。
不仅仅函数能写装饰器,类也可以,但是不常用。
class Foo(object):
def __init__(self,func):
self.func = func
def __call__(self, *args, **kwargs):
print("正在验证身份...")
self.func() """Foo本质是transer=Foo(transer)。
左边的transer是调用__call__魔方方法。
右边的是创建对象,把transer这个函数名传进去,__call__魔方方法才能使用"""
@Foo
def transer():
print("正在转账...") transer() 输出:
正在验证身份...
正在转账...
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
说在最后:
装饰器是Python中一个很重要的东西,经常会用到,而且还意想不到的好用。
装饰器常应用于:
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
python中的闭包与装饰器的更多相关文章
- python中的闭包和装饰器
重新学习完了函数,是时候将其中的一些重点重新捋一捋了,本次总结的东西只有闭包和装饰器 1.闭包 闭包是python函数中的一个比较重要功能,一般闭包都是用在装饰器上,一般学完闭包就会去学习装饰器,这俩 ...
- 21.python中的闭包和装饰器
python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...
- Python 中的闭包与装饰器
闭包(closure)是函数式编程的重要的语法结构.闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性. 如果在一个内嵌函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内嵌函数 ...
- 轻松理解python中的闭包和装饰器 (下)
在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...
- 轻松理解python中的闭包和装饰器(上)
继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...
- 聊聊Python中的闭包和装饰器
1. 闭包 首先我们明确一下函数的引用,如下所示: def test1(): print("--- in test1 func----") # 调用函数 test1() # 引用函 ...
- python中函数总结之装饰器闭包
1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器 ...
- python基础16_闭包_装饰器
不了解是否其他语言也有类似 python 装饰器这样的东西. 最近才发现ECMAScript6也是有生成器函数的,也有 yield generator 装饰器的基础知识是闭包: # 闭包:嵌套函数, ...
- 第十七篇 Python函数之闭包与装饰器
一. 装饰器 装饰器:可以拆解来看,器本质就是函数,装饰就是修饰的意思,所以装饰器的功能就是为其他函数添加附加功能. 装饰器的两个原则: 1. 不修改被修饰函数的源代码 2. 不修改被修饰函数的调用方 ...
随机推荐
- PyQt5图像全屏显示
Windows装这个:https://pypi.python.org/pypi/PyQt5Ubuntu输入这个:sudo apt-get install python3-pyqt5 或者直接输入:pi ...
- c#和html方法互调
具体见连接:https://www.cnblogs.com/zeroLove/p/3912460.html
- JavaScript中注册时间处理程序的方式
基本的方式有两种: 一.第一种方式,出现在Web初期,给时间目标对象或文档元素设置属性. 1.设置JavaScript对象属性为事件处理程序. 示例: 缺点,这种设计都是围绕着假设每个事件目标对于每种 ...
- Flink之状态之savepoint
1.总览 savepoints是外部存储的自包含的checkpoints,可以用来stop and resume,或者程序升级.savepoints利用checkpointing机制来创建流式作业的状 ...
- 【bzoj4817】[Sdoi2017]树点涂色 LCT+LCA+线段树
题目描述 给出一棵n个点,以1为根的有根树,每个点初始染有互不相同的颜色.定义一条路径的权值为路径上的颜色种类数.现有m次操作,每次操作为以下三种之一: 1 x: 把点x到根节点的路径上所有的点染上一 ...
- BZOJ5109 CodePlus 2017大吉大利,晚上吃鸡!(最短路+拓扑排序+bitset)
首先跑正反两遍dij求由起点/终点到某点的最短路条数,这样条件一就转化为f(S,A)*f(T,A)+f(S,B)*f(T,B)=f(S,T).同时建出最短路DAG,这样图中任何一条S到T的路径都是最短 ...
- mysql5.7 MRG集群部署学习
文章目录 1.安装mysql 2.修改配置文件: 3.安装group_replicatin插件,启动group_replication 4.添加节点node-02 node-03: 有关复制组的相关原 ...
- cdq分治入门学习 cogs 1752 Mokia nwerc 2015-2016 G 二维偏序
/* CDQ分治的对象是时间. 即对于一个时间段[L, R],我们取mid = (L + R) / 2. 分治的每层只考虑mid之前的修改对mid之后的查询的贡献,然后递归到[L,mid],(mid, ...
- 【BZOJ 2879】[Noi2012]美食节 费用流
思路同修车,就是多了一个骚气的操作:动态加边,我们通过spfa流的过程可以知道,我们一次只会跑一流量,最后一层边跑过就不会再悔改,所以说我们只会用到一大片里面的很少的点,所以我们如果可以动态加边的话我 ...
- 【NOIP模拟赛】超级树 DP
这个题我在考试的时候把所有的转移都想全了就是新加一个点时有I.不作为II.自己呆着III.连一个IV.连接两个子树中的两个V连接一个子树中的两个,然而V我并不会转移........ 这个题的正解体现了 ...