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("\t" * n, fi)
                read(fi_d, n + 1)  # 继续进行相同的操作
            else:
                print("\t" * 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)

原文地址:https://www.cnblogs.com/tianlangdada/p/11619068.html

时间: 2024-11-04 02:18:07

Python基础(十四)的相关文章

Python 基础语法(四)

Python 基础语法(四) --------------------------------------------接 Python 基础语法(三)-------------------------------------------- 十.Python标准库 Python标准库是随Pthon附带安装的,包含了大量极其有用的模块. 1. sys模块 sys模块包含系统对应的功能 sys.argv ---包含命令行参数,第一个参数是py的文件名 sys.platform ---返回平台类型 sy

初学 Python(十四)——生成器

初学 Python(十四)--生成器 初学 Python,主要整理一些学习到的知识点,这次是生成器. # -*- coding:utf-8 -*- ''''' 生成式的作用: 减少内存占有,不用一次性 创建list中所有的元素,而 是在需要的时候创建 ''' #创建generator有2种方式 #第一种将列表表达式中的[]改为()即可 g = (x*x for x in range(10)) print g for n in g: print n #第二种,关键字yield def fab(ma

Bootstrap&lt;基础十四&gt; 按钮下拉菜单

使用 Bootstrap class 向按钮添加下拉菜单.如需向按钮添加下拉菜单,只需要简单地在在一个 .btn-group 中放置按钮和下拉菜单即可.也可以使用 <span class="caret"></span> 来指示按钮作为下拉菜单. 下面的实例演示了一个基本的简单的按钮下拉菜单: <!DOCTYPE html> <html> <head> <title>Bootstrap 实例 - 基本的按钮下拉菜单&

Python基础知识(四)

Python基础知识(四) 一丶列表 定义格式: 是一个容器,由 [ ]表示,元素与元素之间用逗号隔开. 如:name=["张三","李四"] 作用: 存储任意类型的数据 (32位机器能存5亿多,64为机器存储更多) 特点: 可变 (增,删,改,查) 默认从左到右 ,从0开始 . 有序(索引,切片,步长) 操作: 增 , 删 , 改 ,查 ,索引,切片,步长 ?? #列表的两种定义方式 name=["香蕉","西瓜",&quo

python 基础第四篇

今日大纲: 1. list(增删改查) 列表可以装大量的数据. 不限制数据类型. 表示方式:[] 方括号中的每一项用逗号隔开列表和字符串一样.也有索引和切片常用的功能: 增: append() 删:remove() 改: 索引修改 查: for循环 常用方法: len() count() sort() 排序 2. list的嵌套降维操作3. tuple 元组不可变的列表. 只读列表. 有索引和切片.不可变的是它内部子元素. 如果子元素是列表. 列表中的元素是可以变的.4. range()数数有一

Python基础入门 (四)

一.迭代器&生成器 1.迭代器仅仅是一容器对象,它实现了迭代器协议.它有两个基本方法: 1)next 方法 返回容器的下一个元素 2)_iter_方法 返回迭代器自身.迭代器可以使用内建的iter方法创建 ts = iter(['asd','sds','qweq']) #创建iter方法 print(ts.__next__()) #使用_next_方法返回下一个元素 print(ts.__next__()) print(ts.__next__()) #运行结果 asd sds qweq#需要注意

Python第十四章

通俗的解释:HTML是一个没有穿衣服的人 CSS是穿上了华丽衣服的人 JS是使这个人动起来 HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记).相当于定义统一的一套规则,大家都来遵守他,这样就可以让浏览器根据标记语言的规则去解释它. 例一:模拟web服务器,供浏览器客户端访问 #!/usr/bin/env python # -*- coding:utf-8 -*- import socket def handle_

Python基础第四篇—模块

一.模块 模块,是用一堆代码实现了某个功能的代码集合,模块分为三种:自定义模块(自己定义).内置模块(python自带).开源模块 导入模块 (1).导入一个py文件,解释器解释该py文件 (2).导入一个包,解释器解释该包下的 __init__.py 文件 #模块导入 import module from module.xx import xx from module.xx.xx import * from module.xx.xx import xx as rename #自己给模块定义一个

Python基础-第四天

本篇内容: 1.装饰器 2.列表生成式与生成器 3.可迭代对象与迭代器 4.Python内建函数 一.装饰器 1.装饰器的介绍 装饰器本质是函数,它是为其它函数添加附加功能(装饰其它函数). 装饰器遵循的原则有: ●不能修改被装饰函数的源代码: ●不能修改被装饰函数的调用方式: ●不能修改被装饰函数的运行结果: 2.装饰器的实现方式 ①函数即变量 定义函数就相当于定义变量 >>> variable1 = "python"  # 定义一个变量variable1 >

python 基础(四) 正则,递归 生成器

字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在.比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以复用. 正则表达式是一种用来匹配字符串的强有力的武器.它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的. 下面这张图展示了使用正则表达式匹配的流程 1.Python支持的正则表达式元字符和语法 语法 说明 表