Python闭包与装饰器原理及实例解析


Posted in Python onApril 30, 2020

一、闭包

闭包相当于函数中,嵌套另一个函数,并返回。代码如下:

def func(name): # 定义外层函数
  def inner_func(age): # 内层函数
    print('name: ', name, ', age: ', age)
  return inner_func # 注意此处要返回,才能体现闭包

bb = func('jayson') # 将字符串传给func函数,返回inner_func并赋值给变量
bb(28) # 通过变量调用func函数,传入参数,从而完成闭包
>>
name: jayson , age: 28

二、装饰器

装饰器:把函数test当成变量传入装饰函数deco --> 执行了装饰操作后,变量传回给了函数test()。比如装饰器效果是test = test-1,test函数经过deco装饰后,调用test其实执行的是 test = test-1。

1、装饰器是利用闭包原理,区别是装饰器在闭包中传入的参数是函数,而不是变量。

注:其实在装饰器中,函数即变量

def deco(func): # 传入func函数。
  print('decoration')
  return func
def test():
  print('test_func')

test = deco(test) # 对函数进行装饰。执行了deco函数,并将返回值赋值给test
>>
# 输出deco的运行结果
decoration

test() # 运行装饰后的函数
>>
test_func

2、以上代码等价于

def deco(func): # 传入func函数。
  print('decoration')
  return func

@deco # 等价于上一代码中test = deco(test),不过上一代码需放在定义test之后
def test():
  print('test_func')

>>
# 输出deco的运行结果
decoration

test() # 运行装饰后的函数
>>
test_func

3、装饰器(简版)

def deco(func): # 装饰函数传入func
  print('decoration')
  return func

@deco # 装饰函数。
def test():
  print('test_func') 
# 定义完函数后,会直接执行装饰器deco(test)
>>
decoration

# 调用test,执行test函数
test()
>> 
test_func

3、装饰器(升级版)

在上一个版本中,由于在定义装饰器 + 函数时,就会执行装饰函数里面的语句。

为了使其在未被调用时候不执行,需要再嵌套一个函数,将函数进行包裹。

def deco(func): 
  print('decoration') # 此处未调用func函数时,会直接执行
  def wrapper(): # 名称自定义,一般用wrapper
    print('execute') # 此处未调用func函数时,不会执行
    func() # 执行函数
  return wrapper # 此处返回wrapper给func,通过外部func()执行

@deco # 注意:此处不能有括号。有括号的形式是func未传入最外层deco(),传入deco的子函数中
def test():
  print('test_func')
>>
decoration
#调用test
test()
>>
execute
test_func

注意:如果func函数本身有返回值,同样需要在包裹函数中返回

def deco(func): 
  print('decoration')
  def wrapper():
    print('execute')
    a = func() # 执行函数,并返回值
    print('done')
    return a # 将func的返回值一并返回
  return wrapper

@deco
def test():
  print('test_func')
  return 5 # 增加返回值
>>
decoration

#调用test
test()
>>
execute
test_func
done
 # 此处是test函数的返回值

3、装饰器(进阶版)

在包裹函数中,参数形式设置为*arg、**kwarg,会使得函数更加灵活。

当修改test函数参数形式时,不用在装饰器中同时修改。

import time

def deco(func):
  def inner(*arg, **kwarg): # 此处传入参数
    begin_time = time.time()
    time.sleep(2)
    a = func(*arg, **kwarg) # 调用函数,使用传入的参数
    end_time = time.time()
    print('运行时间:', end_time - begin_time)
    return a
  return inner

@deco
def test(a):
  print('test function:', a)
  return a

# 调用函数
test(5)
>>
test function: 5
运行时间: 2.0003252029418945
 # 5是函数返回的值

4、高阶版

有时候我们会发现有的装饰器带括号,其原因是将上述的装饰器外面又套了一个函数

import time

def outer(): # 在原装饰器外套一层函数,将装饰器封装在函数里面。(outer自定义)
  def deco(func): # 原装饰器,后面的代码一样
    def inner(*arg, **kwarg): 
      begin_time = time.time()
      time.sleep(2)
      a = func(*arg, **kwarg) 
      end_time = time.time()
      print('运行时间:', end_time - begin_time)
      return a
    return inner
  return deco # 注意:此处需返回装饰函数

@outer() # 此处就需要加括号,其实是调用了outer()函数,将test传进其子函数
def test(a):
  print('test function:', a)
  return a

test(4)
>>
test function: 4
运行时间: 2.000566005706787
 # 返回4

5、高阶终结版

带参数的装饰器(装饰器加括号,带参数)

import time

def outer(choose): # 在最外层函数中加入参数
  if choose==1: # 通过choose参数,选择装饰器
    def deco(func):
      def inner(*arg, **kwarg):
        print('decoration1')
        begin_time = time.time()
        time.sleep(2) # 睡眠2s
        a = func(*arg, **kwarg) 
        end_time = time.time()
        print('运行时间1:', end_time - begin_time)
        return a
      return inner
    return deco
  
  else:
    def deco(func):
      def inner(*arg, **kwarg): 
        print('decoration2')
        begin_time = time.time()
        time.sleep(5) # 睡眠5s
        a = func(*arg, **kwarg) 
        end_time = time.time()
        print('运行时间2:', end_time - begin_time)
        return a
      return inner
    return deco

@outer(1) # 由于outer中有参数,此处必须传入参数
def test1(a):
  print('test function1:', a)
  return a

@outer(5) # 传入另一个参数
def test2(a):
  print('test function2:', a)
  return a


# 分别调用2个函数(2个函数装饰器相同,装饰器参数不同)
test1(2) # 调用test1
>>
decoration1
test function1: 2
运行时间1: 2.000072717666626 # 2秒
 # test1的返回值

test2(4) # 调用test2
>>
decoration2
test function2: 4
运行时间2: 5.000797986984253 # 5秒
 # test2的返回值

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现在windows服务中新建进程的方法
Jun 30 Python
Python机器学习之K-Means聚类实现详解
Feb 22 Python
Python线程下使用锁的技巧分享
Sep 13 Python
Python DataFrame.groupby()聚合函数,分组级运算
Sep 18 Python
Python图像处理之图像的读取、显示与保存操作【测试可用】
Jan 04 Python
python操作日志的封装方法(两种方法)
May 23 Python
django 基于中间件实现限制ip频繁访问过程详解
Jul 30 Python
Python获取统计自己的qq群成员信息的方法
Nov 15 Python
最新2019Pycharm安装教程 亲测
Feb 28 Python
python 模拟登陆github的示例
Dec 04 Python
python脚本使用阿里云slb对恶意攻击进行封堵的实现
Feb 04 Python
七个Python必备的GUI库
Apr 27 Python
python+requests接口压力测试500次,查看响应时间的实例
Apr 30 #Python
Pycharm连接远程服务器过程图解
Apr 30 #Python
python3发送request请求及查看返回结果实例
Apr 30 #Python
python获取响应某个字段值的3种实现方法
Apr 30 #Python
如何在python中执行另一个py文件
Apr 30 #Python
在Ubuntu 20.04中安装Pycharm 2020.1的图文教程
Apr 30 #Python
Python实现转换图片背景颜色代码
Apr 30 #Python
You might like
PHP中SESSION使用中的一点经验总结
2012/03/30 PHP
php简单的上传类分享
2016/05/15 PHP
PHP如何使用JWT做Api接口身份认证的实现
2020/02/03 PHP
laravel框架select2多选插件初始化默认选中项操作示例
2020/02/18 PHP
jQuery 方法大全方便学习参考
2010/02/25 Javascript
Iframe自适应高度绝对好使的代码 兼容IE,遨游,火狐
2011/01/27 Javascript
浅谈JS闭包中的循环绑定处理程序
2014/11/09 Javascript
使用Node.js实现HTTP 206内容分片的教程
2015/06/23 Javascript
Bootstrap输入框组件简单实现代码
2017/03/06 Javascript
JS实现仿UC浏览器前进后退效果的实例代码
2017/07/17 Javascript
webpack4 处理CSS的方法示例
2018/09/03 Javascript
用WebStorm进行Angularjs 2开发(环境篇:Windows 10,Angular-cli方式)
2018/12/05 Javascript
JavaScript HTML DOM元素 节点操作汇总
2019/07/29 Javascript
vue 设置 input 为不可以编辑的实现方法
2019/09/19 Javascript
es6函数之严格模式用法实例分析
2020/03/17 Javascript
[00:12]DAC2018 天才少年转战三号位,他的SOLO是否仍如昔日般强大?
2018/04/06 DOTA
Python使用Mechanize模块编写爬虫的要点解析
2016/03/31 Python
python实现输入数字的连续加减方法
2018/06/22 Python
CentOS7下python3.7.0安装教程
2018/07/30 Python
在python中pandas的series合并方法
2018/11/12 Python
python实现将汉字保存成文本的方法
2018/11/16 Python
Python正则表达式学习小例子
2020/03/03 Python
python 等差数列末项计算方式
2020/05/03 Python
浅谈numpy中np.array()与np.asarray的区别以及.tolist
2020/06/03 Python
python uuid生成唯一id或str的最简单案例
2021/01/13 Python
python实现马丁策略回测3000只股票的实例代码
2021/01/22 Python
HTML5 Canvas实现360度全景图的示例代码
2018/01/29 HTML / CSS
本科生个人求职自荐信
2013/09/26 职场文书
同事打架检讨书
2014/02/04 职场文书
运动会入场词50字
2014/02/20 职场文书
社保转移委托书范本
2014/10/08 职场文书
个人股份转让协议书范本
2014/10/26 职场文书
离职员工给领导和同事的感谢信
2015/11/03 职场文书
2016年优秀班主任先进事迹材料
2016/02/26 职场文书
如何用RabbitMQ和Swoole实现一个异步任务系统
2021/05/29 PHP
Python使用socket去实现TCP客户端和TCP服务端
2022/04/12 Python