一、global与nonlocal

  1. #global
  2. x = 1
  3.  
  4. def f1():
  5. global x # 声明此处是全部变量x
  6. x = 2
  7. print(x)
  8.  
  9. f1() # 调用f1后,修改了全局变量x = 2
  10. print(x) # 打印结果为2
  11.  
  12. # nonlocal
  13. def f1():
  14. x = 1
  15. def f2():
  16. nonlocal x # 此处声明使用外层函数的变量x
  17. x = 2 # 将外层函数进行了修改
  18. print(x) #
  19. f2()
  20. print(x) # 因为f1()下的x已经被修改,所以打印结果为2
  21.  
  22. f1()

二、闭包函数

1、什么是闭包函数?

①、根据名词解析:

闭:定义在函数内部的函数,特点是只能在函数内部使用;

包:该函数引用了一个名字,改名字来自函数外层,但又不是全局名称空间

  1. # 闭包函数
  2. def f1()
  3. x = 1
  4. def f2():
  5. print(x)
  6. f2()
  7. # f2()就是一个闭包函数

②、闭包函数升级:能够在全局名称空间使用闭包函数名称

在闭包函数中,使用了函数嵌套,函数名称空间与定义域的知识,要想能够在全局定义域使用闭包函数,就需要使用函数对象的知识

函数可以如变量一般被引用、被当做参数,当然也能被用来赋值给变量,函数名实质上绑定了存放函数内存空间的内存地址,在不+()的情况下,我们得到的是内存地址,就可以赋值到全局名称空间

  1. # 闭包函数
  2. def f1()
  3. x = 1
  4. def f2():
  5. print(x)
  6. return f2 # 此处返回函数f2的内存地址,千万不要加括号
  7.  
  8. f = f1() # f ------>调用f1()----->返回f2内存地址
  9. f() # 此时,f指向了f2的内存地址,并且是全局名称空间的名称

③为函数传参的两种方式

  • 直接以参数的形式传入
  • 通过闭包函数的形式包给函数

  1. # 给函数传参的两种方式
  2. #方式一
  3. def f1(x):
  4. print(x)
  5.  
  6. f1("ksdffbksd")
  7.  
  8. #方式二
  9. def f2():
  10. x = 2
  11. def f3():
  12. print(x)
  13. f2()
  14.  
  15. f2()

三、装饰器

1、什么是装饰器

装饰器思想:在开放封闭原则下为被修饰对象添加新功能,所谓开放原则是对拓展新功能是开放的,封闭原则是指对源代码不进行修改,不改变源代码调用方式。

实际生产中,如果需要对以上线的程序添加新功能,但是为了保证程序运行的稳定性与安全性,会根据装饰器思想进行添加。

装饰器:指创建一个工具,在遵循以下原则前提下,为被装饰对象添加新功能

  • 原则1:不修改被装饰对象源代码
  • 原则2:不修改被装饰对象调用方式

2、装饰器详解

现有需求:为函数增加计时功能,该如何实现?

    1. 方案一:修改源代码
  1. # 方案一:修改源代码
  2. def f1():
  3. start = time.time()
  4. print("hhhh")
  5. end = time.time()
  6. time.sleep(0.8)
  7. print("run time is %s" %(end - start))
  8.  
  9. f1()
  10. # 改变了源代码,没有改变调用方式
    1. 方案二:在函数后增加代码
  1. #方案二:在函数后增加代码
  2. start = time.time()
  3. f1()
  4. end = time.time()
  5. print("run time is %s" %(end - start))
  6. #没有改变源代码,也没有改变调用方式,但是需要重复代码,并且比较复杂
    1. 方案三:将后面的功能代码写成函数
  1. # 方案三:将后面的功能代码写成函数
  2. def f2():
  3. start = time.time()
  4. f1()
  5. end = time.time()
  6. print("run time is %s" % (end - start))
  7.  
  8. f2()
  9. # 没有改变源代码,但是改变了调用方式
    1. 方案四:使用闭包函数,可以实现不改变源代码,并且不改变调用方式
  1. # 方案四使用闭包函数,可以实现不改变源代码,并且不改变调用方式
  2. def outter():
  3. def f2():
  4. start = time.time()
  5. f1()
  6. end = time.time()
  7. print("run time is %s" % (end - start))
  8. return f2 # 我们需要返回封装好的函数内存地址,所以千万不要加()
  9.  
  10. f = outter()
  11. f()
  12. #实现了添加功能,也没有改变源代码,但是调用方式改变了
  • 方案五:使用修改后的闭包函数
  • #使用修改后的闭包函数
    def outter():
    def f2():
    start = time.time()
    f1()
    end = time.time()
    print("run time is %s" % (end - start))
    return f2

    f1 = outter() # 可以在顶级域中,将f2的内存地址赋值给f1,此时,原函数调用没有改变
    f1()
    #注意,此处的f1不是原来的f1,而是经过“打包”后的f2,只是名字也叫f1
    # 我们还发现一个问题,这个闭包函数只可以对f1有用,但是我们需要一个能够通用的工具

  • 方案六:通用装饰器

  1. #通用装饰器
  2. def outter(func):
  3. def f2():
  4. start = time.time()
  5. func()
  6. end = time.time()
  7. print("run time is %s" % (end - start))
  8. return f2
  9.  
  10. f1 = outter(f1)
  11. f1()
  12. # 进一步的,我们将函数名按照常用的规范改写
  13. def outter(func):
  14. def wrapper():
  15. start = time.time()
  16. func()
  17. end = time.time()
  18. print("run time is %s" % (end - start))
  19. return wrapper
  20.  
  21. f1 = outter(f1)
  22. f1()
  23. #在这里,f1()是没有参数的,如果f1是有参数的呢?
  • 方案七:有参函数的装饰器

  1. # 有参函数的装饰器
  2. def print1(name):
  3. print(name)
  4. time.sleep(1)
  5.  
  6. def outter(func):
  7. def wrapper(name): # 注意这里和无参函数的区别,由于我们之前的操作就是将封装好的wrapper函数重新复制,
  8. # 但是实质还是wrapper,所以我们要在这里添加一个参数,为函数体内的修饰对象func()传参
  9. start = time.time()
  10. func(name) # 将wrapper接受的参数放进我们所修饰的对象
  11. end = time.time()
  12. print("run time is %s" % (end - start))
  13. return wrapper
  14.  
  15. print1 = outter(print1)
  16. print1("hadsbf")
  17. #上面将装饰器写死了,只能对print1函数有效,如何做到对所有函数有效呢?
  • 方案八:有参函数装饰器改进,使用*与**

  1. # 有参函数装饰器改进,使用*与**
  2. def print1(name):
  3. print(name)
  4. time.sleep(1)
  5.  
  6. def outter(func):
  7. def wrapper(*args,**kwargs): # 由于我们不知道函数有多少参数,所以要用到*args与**kwargs来接受可变数量的参数
  8. start = time.time()
  9. func(*args,**kwargs) # 这里将上面的实参放到函数内,代表将传递过来的参数打散,最后的效果就是原样传递
  10. end = time.time()
  11. print("run time is %s" % (end - start))
  12. return wrapper
  13.  
  14. print1 = outter(print1)
  15. print1("hadsbf")
  16. # 此时,装饰器无论对于有参函数,还是无参函数都同样适用,但是如果有返回值呢?
  • 方案九:有返回值函数的装饰器

  1. # 有返回值函数的装饰器
  2. def print1(name):
  3. print(name)
  4. time.sleep(1)
  5. return 111
  6.  
  7. def outter(func):
  8. def wrapper(*args,**kwargs):
  9. start = time.time()
  10. res = func(*args,**kwargs) # 我们需要一个变量接受函数的返回值
  11. end = time.time()
  12. print("run time is %s" % (end - start))
  13. return res # 我们装饰器的核心是wrapper函数,只需要在wrapper函数中提供返回值即可
  14. return wrapper
  15.  
  16. print1 = outter(print1)
  17. print1("hadsbf")
    1. python语法糖:python官方提供的一种简洁定义装饰器的语法,使用@

  1. # python语法糖
  2. def outter(func):
  3. def wrapper(*args,**kwargs):
  4. start = time.time()
  5. res = func(*args,**kwargs) # 我们需要一个变量接受函数的返回值
  6. end = time.time()
  7. print("run time is %s" % (end - start))
  8. return res # 我们装饰器的核心是wrapper函数,只需要在wrapper函数中提供返回值即可
  9. return wrapper
  10.  
  11. @outter # @+装饰器名称,可以快速完成 pint1 = outter(print1)的函数赋值
  12. def print1(name):
  13. print(name)
  14. time.sleep(1)
  15. return 111
  • python装饰器模板

  1. # 装饰器标准模板
  2. def outter(func):
  3. def wrapper(*args,**kwargs):
  4. res = func(*args,**kwargs)
  5. return res
  6. return wrapper
  7. # 可根据实际的需求对模板进行修改
    1. 多层装饰器嵌套

  1. # 多层装饰器嵌套
  2. def outter1(func1):
  3. def wrapper1(*args,**kwargs):
  4. print("11111")
  5. res1 = func1(*args,**kwargs)
  6. return res1
  7. return wrapper1
  8.  
  9. def outter2(func2):
  10. def wrapper2(*args,**kwargs):
  11. print("22222")
  12. res2 = func2(*args,**kwargs)
  13. return res2
  14. return wrapper2
  15.  
  16. def outter3(func3):
  17. def wrapper3(*args,**kwargs):
  18. print("333333")
  19. res3 = func3(*args,**kwargs)
  20. return res3
  21. return wrapper3
  22.  
  23. @outter1
  24. @outter2
  25. @outter3
  26. def print1():
  27. print("444444")

当出现多个装饰器时,代码越靠后,则越先封装,如图,首先使用outter3封装print1(),可以看做一个整体:f3,然后再使用outter2修饰f3,修饰后可以看做一个整体:f2,最后使用outter1修饰f2,看做整体f1,所以最先运行的是f1,也就是outter1,遇到func1,则跳转到其修饰的outter2,运行wrapper2,遇到func2,则跳转到outter3,运行wrapper3,遇到func3,则跳转到源代码print1,运行完毕后,返回wrapper3,运行ruturn,退出outter3,如此往后逐层退出,最后结果为:

python函数之闭包函数与无参装饰器的更多相关文章

  1. python语法糖之有参装饰器、无参装饰器

    python的装饰器简单来说就是函数的一种形式,是为了扩展原来的函数功能而设计的. 装饰器的特别之处在于它的返回值也是一个函数,可以在不改变原有函数代码的基础上添加新的功能 # 先定义一个函数及引用# ...

  2. PYTHON-有参装饰器,无参装饰器,语法糖

    装饰器 装饰器就是闭包函数的一种应用场景 一 为何要用装饰器 #开放封闭原则:对修改封闭,对扩展开放 二 什么是装饰器 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象. 强 ...

  3. day11有参装饰器,无参装饰器

    今日内容 1.有参装饰器 2.无参装饰器 什么是装饰器? 用来为被装饰对象添加新功能的工具. 注:装饰器可以是任意可调用对象,被装饰对象也可以是任意可调用对象. 为何要用装饰器? 开放封闭原则:对修改 ...

  4. Python无参装饰器

    需求:想要在test_func函数前后执行一些代码   1.第一步(定义函数,将调用原函数,使用新函数替换) def test_func(): return 'test_func' def test_ ...

  5. python中的无参装饰器和有参装饰器

    python中的无参装饰器和有参装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 装饰器特点: 1>.开放封闭原则,即对扩展是开放的,对修改时封闭的: 2>.装饰器 ...

  6. Python练习-无参装饰器的正确打开方式

    import time def DecoUserPrint(UserFunc):#定义一个DecoUserPrint接收参数的多重方法 def DecoPrint(): StartTime = tim ...

  7. python函数之有参装饰器

    一.为什么要有有参装饰器? 来看之前的无参装饰器 # 无参装饰器 def outter(func): def wrapper(*args,**kwargs): start = time.time() ...

  8. python函数:装饰器、修正、语法糖、有参装饰器、global与nonlocal

    一.装饰器 二.装饰器修正1 三.装饰器修正2 四.装饰器的语法糖 五.有参.无参装饰器 六.global与nonlocal 一.装饰器 ''' 1 什么是装饰器 器=>工具 装饰=>指的 ...

  9. 12、Python函数高级(命名空间、作用域、装饰器)

    一.名称空间和作用域 1.命名空间(Namespace) 命名空间是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的. 命名空间提供了在项目中避免名字冲突的一种方法.各个命名空 ...

随机推荐

  1. how to fetch a group promise api in order with the returned resolved result

    how to fetch a group promise api in order with the returned resolved result promise 一组依次请求,generator ...

  2. WebVR & CSS 3D & WebGL

    WebVR & CSS 3D & WebGL VR https://developer.mozilla.org/en-US/docs/Web/API/WebVR_API https:/ ...

  3. js function call hacker

    js function call hacker you don't know javascript function https://developer.mozilla.org/en-US/docs/ ...

  4. TypeScript——02——TS基本数据类型介绍和使用

    一,TS的数据类型 ES6的数据类型: 6种基本数据类型 Boolean Number String Symbol undefined null 3种引用类型 Array Function Objec ...

  5. Redis的数据结构与应用场景

    一.Redis简介 Redis 是一个开源的使用 ANSI C 语言编写.遵守 BSD 协议.支持网络.可基于内存.分布式.可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API ...

  6. redis五种数据类型的应用

    redis的五种数据类型和使用场景 string类型 string类型多用于缓存 set key value(value可以为json字符串) setnx多用于分布式锁(后面详细整理) 计数器 inc ...

  7. Oracle VM VirtualBox安装CentOS7

    安装VirtualBox6.0 下载地址:https://www.virtualbox.org/ 新建虚拟机 类型:Linux 版本:Other Linux(64-bit)----如果没有出现64-b ...

  8. 使用gitlab构建基于docker的持续集成(一)

    使用gitlab构建基于docker的持续集成(一) gitlab docker aspnetcore 持续集成 开篇 整体环境规划 准备工作 CA证书 虚拟机系统:安装Centos7.3 3.设置C ...

  9. Django中文文档-模型Models(二):Meta选项、模型属性、模型方法

    元数据(Meta)选项 使用内部的class Meta 定义模型的元数据,例如: from django.db import models class Ox(models.Model): horn_l ...

  10. 阿里巴巴Druid,轻松实现MySQL数据库连接加密!

    为什么要加密? 现在的开发习惯,无论是公司的项目还是个人的项目,都会选择将源码上传到 Git 服务器(GitHub.Gitee 或是自建服务器),但只要将源码提交到公网服务器就会存在源码泄漏的风险,而 ...