zoukankan      html  css  js  c++  java
  • Python基础(十三)

    今日主要内容

    • 闭包
    • 装饰器初识
    • 标准装饰器

    一、闭包

    (一)什么是闭包

    1. 闭包:内层函数调用外层函数的变量就是闭包(不能是全局变量)

      def func1():
      	a = 10
      	def func2():
      		print(a)  # 内层函数调用外层函数的变量,这就是一个闭包
      
      func1()
      
    2. 检测闭包的方法

      • 函数名.__closure__
      • 若返回对象地址就是一个闭包,返回None就不是一个闭包
      • 注意:.__closure__检测的是函数名,判断这个函数是否是闭包
      def func1():
      	a = 10
      	def func2():
      		print(a)  # 调用外层变量a,是闭包
      	print(func2.__closure__)  # 判断func2是否是闭包
      
      func1()
      
      运行结果:
      (<cell at 0x00000230F21874F8: int object at 0x0000000063258190>,)
      
      def func1():
          a = 10
          def func2():
              a = 10
              print(a)  # 使用自身函数变量a,不是闭包
          print(func2.__closure__)
      
      func1()
      
      运行结果:
      None
      
    3. 如何在全局空间中调用内部函数

      • 外层函数返回内层函数的函数名(内存地址),就可在全局空间中调用内部函数
      def func1():
      	a = 10
      	def func2():
      		print(a)
      	return func2  # 返回内层函数的函数名
      	
      f = func1()  # 此时f就是func2
      f()  # 调用内层函数func2
      print(f.__closure__)
      
      运行结果:
      10
      (<cell at 0x00000280A87574F8: int object at 0x0000000063258190>,)
      

    (二)闭包的作用

    1. 保护数据安全
    • 说白话一点,如果数据放在全局变量中(顶格写代码),数据的安全性很低,因为所有人都可以无意间修改它,想解决这个问题我们可以把数据放到函数中,只要不调用这个函数,我的数据就是安全的。但是有个问题,每次执行函数完,解释器就会自动清空此函数开辟的局部空间中所有内容(包括数据开辟的空间),所以每次调用完函数,我的数据就没了,此时就需要利用到了闭包。闭包可以在内层函数调用外层函数中的变量,而且内层函数如果调用了外层函数中的变量,那这个变量将不会消亡,将会常驻内存。所以,我们只需要在全局空间中调用这个闭包的内层函数,就可以使用我的数据了,而且数据的安全性也提高了。
    1. 将变量常驻内存,供后续代码使用

      def outer():
      	lst = [1,2,3,4,5]
      	def inner():
      		return lst
      	return inner
      	
      f = outer()
      f_lst = f()
      print(f_lst)
      
      运行结果:
      [1,2,3,4,5]
      

    (三)闭包的应用

    • 防止数据被误修改
    • 装饰器(与闭包格式相同)

    二、装饰器初识

    (一)软件开发的六大原则(了解)

    1. 开闭原则(Open Close Principle)——装饰器依据
    2. 里氏代换原则(Liskov Substitution Principle)
    3. 依赖倒转原则(Dependence Inversion Principle)
    4. 接口隔离原则(Interface Segregation Principle)
    5. 迪米特法则(最少知道原则)(Demeter Principle)
    6. 合成复用原则(Composite Reuse Principle)

    (二)装饰器依据——开闭原则

    • 开放封闭原则:
      • 对功能扩展开放
      • 对源码修改封闭

    (三)装饰器引入

    • 相信大多数人都玩LOL,我们模拟一次游戏过程:

      • 向平常一样的流程,十连跪...
      def play_lol():
      	print("登陆游戏")
      	print("开始排位...")
      	print("游戏中...")
      	print("失败...")
      	print("结束游戏")
      
      play_lol()
      
      运行结果:
      登陆游戏
      开始排位...
      游戏中...
      Virtory
      结束游戏
      
      • 受不了了,开一个外挂吧,此时我的函数需要扩展,在前后添加开启关闭外挂的功能
      def play_lol():
      	print("开启外挂!")  # 添加开启外挂功能
      	print("登陆游戏")
      	print("开始排位...")
      	print("游戏中...")
      	print("胜利!!!")
      	print("结束游戏")
      	print("关闭外挂!")  # 添加关闭外挂功能
      
      play_lol()
      
      运行结果:
      开启外挂!
      登陆游戏
      开始排位...
      游戏中...
      胜利!!!
      结束游戏
      关闭外挂!
      
      • 但此时,违背了开闭原则,对源代码进行了修改。想一个方法,对源代码进行前后包装,不改变源码
      def play_lol():
      	print("登陆游戏")
      	print("开始排位...")
      	print("游戏中...")
      	print("胜利!!!")
      	print("结束游戏")
      	
      def new_play():  # 将上面代码前后包装成了一个新的函数,没有改变源码
      	print("开启外挂!")
      	play_lol()
      	print("关闭外挂!")
      	
      new_play()
      
      运行结果:
      开启外挂!
      登陆游戏
      开始排位...
      游戏中...
      胜利!!!
      结束游戏
      关闭外挂!
      
      • 功能实现了,而且还没有改变源码,但是有一个问题,我们之前访问调用的是play_lol这个函数,但此时我们访问调用的是new_play()这个函数,相当于改变了调用,还是违背了开闭原则,没有达到扩展的效果,此时我们就需要对这段代码稍作变化
      def play_lol():
      	print("登陆游戏")
      	print("开始排位...")
      	print("游戏中...")
      	print("胜利!!!")
      	print("结束游戏")
      	
      def wrapper(fn):    # 装饰器雏形
      	def inner():
      		print("开启外挂!")
      		fn()
      		print("关闭外挂!")
      	return inner
      
      func = wrapper(play_lol)  # 调用装饰器函数将我基础函数传入进去包装
      play_lol = func  # 返回的是包装函数,将包装函数重命名成我原来的函数
      play_lol()  # 调用此函数
      
      运行结果:
      开启外挂!
      登陆游戏
      开始排位...
      游戏中...
      胜利!!!
      结束游戏
      关闭外挂!
      
      • 上述代码就引出了装饰器的雏形,刨析一下:
        • 装饰器雏形:与闭包的格式相同,两层函数构成:
          • 内层函数就是我的包装函数,将扩展的功能和原函数包在一起组成一个函数
          • 外层函数的作用就是给内层函数传参用的,传入的是我原函数的函数名,在内层调用
        • 装饰器的返回值 return inner:装饰器的返回值是内层函数的函数名,真正进行包装扩展的是内层函数
        • 将返回值重命名成原函数名:将返回值重命名成原来的函数名,其实就是把真正作用的包装函数重命名成原来的函数名,所以就解决了调用新函数的问题,真正遵循了开闭原则,再次调用原来的函数其实真正运行的是装饰器内部的inner函数
      def wrapper(fn):    # 装饰器雏形
      	def inner():
      		print("开启外挂!")
      		fn()
      		print("关闭外挂!")
      	return inner
      
      func = wrapper(play_lol)  # 调用装饰器函数将我基础函数传入进去包装
      play_lol = func  # 返回的是包装函数,将包赚函数重命名成我原来的函数
      play_lol()  # 调用此函数
      
      • 利用语法糖装饰

        • 使用装饰器的两行代码可以转换成语法糖
        func = wrapper(play_lol)  # 调用装饰器函数将我基础函数传入进去包装
        play_lol = func  # 返回的是包装函数,将包赚函数重命名成我原来的函数
        
        • 语法糖
        @wrapper  # 语法糖
        def play_lol():
        	print("登陆游戏")
        	print("开始排位...")
        	print("游戏中...")
        	print("胜利!!!")
        	print("结束游戏")
        
      • 此时,装饰器的雏形就出来了

    • 装饰器雏形

      def wrapper(fn):
      	def inner():
      		"""扩展功能"""
      		fn()
      		"""扩展功能"""
      	return inner
      	
      @wrapper
      def func():
      	pass
      	
      func()
      
    • 游戏模拟继续进行

      • 就算开挂,我们也得选完英雄,才能进入游戏,所以我们给基础函数传个参数,但是我们真正执行的是装饰器内部的inner包装函数,所以也要给inner传入参数
      def wrapper(fn):    # 装饰器雏形
      	def inner(hero):  # 套到装饰器中内层包装函数参数
      		print("开启外挂!")
      		fn(hero)  # 基础函数参数
      		print("关闭外挂!")
      	return inner
      
      @wrapper
      def play_lol(hero):  # 基础函数参数
      	print("登陆游戏")
      	print("开始排位...")
      	print(f"选择英雄:{hero}")
      	print("游戏中...")
      	print("胜利!!!")
      	print("结束游戏")
      
      play_lol("盖伦")
      
      运行结果:
      开启外挂!
      登陆游戏
      开始排位...
      选择英雄:盖伦  # 传入的参数
      游戏中...
      胜利!!!
      结束游戏
      关闭外挂!
      
      • 虽然开挂了,但是还是会遇到巨坑无敌坑的队友,我们得把他记下来举报他,所以要给基础函数填写返回值,同时给真正执行的inner函数填写返回值
      def wrapper(fn):    # 装饰器雏形
      	def inner(hero):  
      		print("开启外挂!")
      		ret = fn(hero)  # 接收基础函数的返回值
      		print("关闭外挂!")
      		return ret  # 返回包装后的函数的返回值
      	return inner
      
      @wrapper
      def play_lol(hero):  # 基础函数参数
      	print("登陆游戏")
      	print("开始排位...")
      	print(f"选择英雄:{hero}")
      	print("游戏中...")
      	print("胜利!!!")
      	print("结束游戏")
      	return "坑比队友:xxx"  # 基础函数返回值
      
      print(play_lol("盖伦"))
      
      运行结果:
      开启外挂!
      登陆游戏
      开始排位...
      选择英雄:盖伦
      游戏中...
      胜利!!!
      结束游戏
      关闭外挂!
      坑比队友:xxx  # 返回值
      
      • 到此,我们装饰器标准模式也就出来了
    • 装饰器标准模式(非常重要)

      def wrapper(fn):
      	def inner(*args, **kwargs):
      		"""扩展功能"""
      		ret = fn(*args, **kwargs)
      		"""扩展功能"""
      		return ret
      	return inner
      	
      @wrapper
      def func():
      	pass
      
      func()
      
  • 相关阅读:
    IIS服务器SSL证书安装 (pfx文件不能直接运行时)
    .NET Core创建Worker Services
    1.初步了解IOC容器
    1.Configuration
    2.第一个Codefirst实例
    安装Mysql
    2.我的第一个小程序(获取用户信息--包括敏感信息)
    1.什么是微信小程序
    Repeater的使用
    1.MVC的初步了解
  • 原文地址:https://www.cnblogs.com/tianlangdada/p/11616788.html
Copyright © 2011-2022 走看看