• Python基础(十四)


    今日主要内容

    • 装饰器扩展
      • 有参装饰器
      • 多个装饰器装饰一个函数
    • 递归

    一、装饰器扩展

    (一)含有参数的装饰器

    • 先来回顾一下装饰器的标准模式

      def wrapper(fn):
      	def inner(*args, **kwargs):
      		"""扩展内容"""
      		ret = fn(*args, **kwargs)
      		"""扩展内容"""
      	return inner
      
      @wrapper
      def func():
      	pass
      	
      func()
      
    • 回顾一下之前的游戏模拟,通过装饰器给我的游戏过程扩展了开挂的功能,装饰之后每次想玩游戏的时候调用函数都会给你先把挂打开,此时你的游戏函数已经被装饰了,但是现在有一个问题,我今天想自己玩一把,不想开挂了,怎么办?我们可以给装饰器传一个参数,来控制我的装饰器的开启和关闭就可以了

      def wrapper_outer(argv):  # 给装饰器加一个参数,控制装饰器开启关闭
          def wrapper(fn):
              def inner(hero): 
                  if argv:  # 如果是True执行添加装饰
                      print("开启外挂!")
                      ret = fn(hero)
                      print("关闭外挂!")
                      return ret
                  else:  # 如果是False,执行原函数
                      ret = fn(hero)
                      return ret
              return inner
          return wrapper
      
      @wrapper_outer(True)
      def play_lol(hero):  # 基础函数参数
      	print("登陆游戏")
      	print("开始排位...")
      	print(f"选择英雄:{hero}")
      	print("游戏中...")
      	print("胜利!!!")
      	print("结束游戏")
      	return "坑比队友:xxx"  # 基础函数返回值
      
      print(play_lol("盖伦"))
      
      运行结果:
      开启外挂!
      登陆游戏
      开始排位...
      选择英雄:盖伦
      游戏中...
      胜利!!!
      结束游戏
      关闭外挂!
      坑比队友:xxx
      
      • 刨析一下:

        • 先来看装饰器和语法糖
          • @wrapper_outer(True)先执行函数调用,函数调用返回的是我内层装饰器的函数名,相当于@wrapper
          • 装饰器最外层的参数控制内层包装函数inner里面的函数部分是否执行,如果argv为真,相当于执行了包装,如果argv为假,执行原函数
        • 通过给装饰器传参起到了控制装饰器是否生效的功能
        def wrapper_outer(argv):
            def wrapper(fn):
                def inner(hero):  
                    if argv:  # 为真执行这里
                        print("开启外挂!")
                        ret = fn(hero)
                        print("关闭外挂!")
                        return ret
                    else:  # 为假执行这里
                        ret = fn(hero)
                        return ret
                return inner
            return wrapper
        
        @wrapper_outer(True)  # 先执行函数调用
        
        • 注意:一旦给函数装饰过,装饰器的参数是不能变化的,因为闭包的原因参数已经被闭进去了,只能调用内层函数,无法再修改最外层的装饰器参数
        flag = True
        def wrapper_outer(argv):
            def wrapper(fn):
                def inner(*args, **kwargs):  
                    if argv:
                        """扩展功能"""
                        ret = fn(*args, **kwargs)
        				"""扩展功能"""
                        return ret
                    else:
                        ret = fn(*args, **kwargs)
                        return ret
                return inner
            return wrapper
        
        @wrapper_outer(flag)
        def func():
            pass
        
        flag = False
        func()  # 此时flag依然是True,装饰过就不能修改参数的值
        
    • 有参装饰器的标准模式

      def wrapper_outer(argv):
          def wrapper(fn):
              def inner(*args, **kwargs):  
                  if argv:
                      """扩展功能"""
                      ret = fn(*args, **kwargs)
      				"""扩展功能"""
                      return ret
                  else:
                      ret = fn(*args, **kwargs)
                      return ret
              return inner
          return wrapper
      
      @wrapper_outer(True)
      def func():
          pass
      
      func()
      

    (二)多个装饰器装饰一个函数

    • 执行原理:从里到外进行包装

      def wrapper1(fn):
      	def inner(*args, **kwargs):
      		print("扩展功能1")
      		ret = fn(*args, **kwargs)
      		print("扩展功能4")
      		return ret
      	return inner
      	
      def wrapper2(fn):
      	def inner(*args, **kwargs):
      		print("扩展功能2")
      		ret = fn(*args, **kwargs)
      		print("扩展功能3")
      		return ret
      	return inner
      	
      @wrapper1
      @wrapper2
      def func():
      	print("目标函数")
      
      func()
      
      运行结果:
      扩展功能1
      扩展功能2
      目标函数
      扩展功能3
      扩展功能4
      
      • 刨析一下:

        • 从里往外看,先用第一层装饰器@wrapper2装饰目标函数func(),装饰完将其看作成一个整体,在被上层装饰器@wrapper1装饰
        • 返回值:执行完目标函数,将目标函数的返回值先反给最近的装饰器@wrapper2内部的inner包装函数中,之后再将@wrapper2内部的inner包装函数的返回值返回给上一层装饰器@wrapper1内部的inner中,最终得到的返回值是我调用函数的返回值
        • 最终调用目标函数其实真正执行的是最外层装饰器中的包装函数inner,而最外层装饰器中的包装函数inner包装着内层装饰器的包装函数inner,而内层装饰器的包装函数inner包装着真正的目表函数func
        # 伪代码:
        
        def 装饰器1(传入目标函数):
        	def 内层包装函数1,也是真正执行的函数(目标函数的参数):
        		"""前扩展功能"""
        		目标函数(目标函数的参数)
        		"""后扩展功能"""
        	return 包装函数的函数名
        
        def 装饰器2(传入目标函数):
        	def 内层包装函数2,也是真正执行的函数(目标函数的参数):
        		"""前扩展功能"""
        		目标函数(目标函数的参数)
        		"""后扩展功能"""
        	return 包装函数的函数名
        	
        @装饰器1
        @装饰器2
        def 目标函数(形参):
        	函数体
        	
        目标函数(实参)
        
        # 真正执行过程:
        先执行:装饰器1的内层包装函数1,而传入的目标函数是:装饰器2的内层包装函数2
        再执行:装饰器2的内层包装函数2,而传入的目标函数是:目标函数
        

    二、递归

    (一)什么是递归

    • 首先递归是一个函数,只要满足两个要求的函数就是递归函数:
      • 不断调用自己本身
      • 有明确的结束条件

    (二)递归深度

    • 如果只是在不断的调用自己本身,没有一个明确的结束条件,那么就是一个死递归(无限循环)。

    • Python官方规定,为了避免无限制的调用自己本身,递归的最大深度为1000(最多只能调用自己本身1000次),实际递归深度为998

      def func():
          print(1)
          func()
      
      func()
      
      运行结果:
      [Previous line repeated 994 more times]
      1
      1
      1
      ...(共打印998个1)
      
    • 可以通过导入sys模块,修改最大递归深度

      import sys
      sys.setrecursionlimit(100)  # 修改递归深度
      
      def func():
          print(1)
          func()
      
      func()
      
      运行结果:
      [Previous line repeated 94 more times]
      1
      1
      1
      ...(实际打印98个1)
      

    (三)递归的应用

    1. 求n的阶乘

      def factorial(n):
          if n == 1:
              return 1
          return factorial(n - 1) * n
      
      print(factorial(5))
      
      运行结果:
      120
      
    2. 计算斐波那契序列

      def fib(n):
      	if n <= 2:
      		return 1
      	return fib(n-1) + fib(n-2)
      	
      print(list(map(fib,range(1, 6))))
      
      运行结果:
      [1, 1, 2, 3, 5]
      
    3. 打印列表嵌套的每一个元素

      l1 = [1, 2, [3, 4, [5, [6, 7, [8, 9], 10], 11, 12], 13], 14, 15]
      
      def func(lst):
          for el in lst:
              if type(el) == list:
                  func(el)
              else:
                  print(el, end=" ")
      
      func(l1)
      
      运行结果:
      1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
      
    4. 给列表去重,不能使用集合

      l1 = [1, 1, 2, 3, 4, 5, 6, 3, 3, 5, 6, 3, 4, 5]
      
      def del_repetition(lst):
          for el in lst:
              if lst.count(el) > 1:
                  lst.remove(el)
                  del_repetition(lst)
      
      del_repetition(l1)
      print(l1)
      
      运行结果:
      [1, 2, 6, 3, 4, 5]
      
    5. 遍历文件夹中所有的文件

      import os
      
      def read(filepath, n):
          files = os.listdir(filepath)  # 获取到当前文件夹中的所有文件
          for fi in files:  # 遍历文件夹中的文件, 这里获取的只是本层文件名
              fi_d = os.path.join(filepath, fi)  # 加入文件夹 获取到文件夹文件
              if os.path.isdir(fi_d):  # 如果该路径下的文件是文件夹
                  print("	" * n, fi)
                  read(fi_d, n + 1)  # 继续进行相同的操作
              else:
                  print("	" * n, fi)  # 递归出口. 最终在这里隐含着return
      
      # 递归遍历目录下所有文件
      read('../day16/', 0)
      
    6. 二分查找

      # 普通递归版本⼆二分法
      lst = [22, 33, 44, 55, 66, 77, 88, 99, 101, 238, 345, 456, 567, 678, 789]
      n = 567
      left = 0
      right = len(lst) - 1
      
      def binary_search(n, left, right):
          if left <= right:
              middle = (left + right) // 2
              if n < lst[middle]:
                  right = middle - 1
              elif n > lst[middle]:
                  left = middle + 1
              else:
                  return middle
              return binary_search(n, left, right)  
              # 这个return必须要加. 否则接收到的永远是None.
          else:
              return -1
      
      print(binary_search(567, 0, len(lst) - 1))
      
    7. 三级菜单进入返回

      menu = {
          '北京': {
              '海淀': {
                  '五道口': {
                      'soho': {},
                      '网易': {},
                      'google': {}
                  },
                  '中关村': {
                      '爱奇艺': {},
                      '汽车之家': {},
                      'youku': {},
                  },
                  '上地': {
                      '百度': {},
                  },
              },
              '昌平': {
                  '沙河': {
                      '北邮': {},
                      '北航': {},
                  },
                  '天通苑': {},
                  '回龙观': {},
              },
              '朝阳': {},
              '东城': {},
          },
          '上海': {
              '闵行': {
                  "人民广场": {
                      '炸鸡店': {}
                  }
              },
              '闸北': {
                  '火车战': {
                      '携程': {}
                  }
              },
              '浦东': {},
          },
          '天津': {
              "和平": {
                  "小白楼": {},
                  "五大道小洋楼": {},
                  "滨江道": {},
              },
              "南开": {
                  "天大": {},
                  "南开": {},
                  "理工": {},
              },
              "河北": {
                  "天津之眼": {},
                  "海河": {},
                  "意式风情区": {},
                  "世纪钟": {},
                  "大悲院": {},
              },
          },
      }
      
      def menu_func(menu):
          while True:
              for k in menu:
                  print(k)
              key = input('input>>').strip()
              if key == 'b' or key == 'q':
                  return key
              elif menu.get(key):
                  ret = menu_func(menu[key])
                  if ret == 'q': 
                      return 'q'
      
      menu_func(menu)
      
  • 相关阅读:
    leetcode 686. Repeated String Match
    leetcode 507. Perfect Number
    leetcode 681. Next Closest Time
    leetcode 682. Baseball Game
    leetcode 684. Redundant Connection
    leetcode 677. Map Sum Pairs
    leetcode 402. Remove K Digits
    python numpy
    leetcode 409. Longest Palindrome
    python垃圾回收机制
  • 原文地址:https://www.cnblogs.com/tianlangdada/p/11619068.html
Copyright © 2020-2023  润新知