• Python基础学习笔记(19)re 模块 递归函数 带参数的装饰器


    Python基础学习(19)re 模块Ⅱ、递归函数、带参数的装饰器

    一、今日大纲

    • re 模块
    • 递归函数
    • 带参数的装饰器

    二、re 模块Ⅱ

    1. split()

      在 re 模块中,split()主要用于将一段字符串根据正则表达式特征匹配分离,也可以定义分组返回特定的匹配字符串。

      ret1 = re.split('d+', 'alex222wusir111taibai')
      ret2 = re.split('(d+)', 'alex222wusir111taibai')
      ret3 = re.split('d(d)d', 'alex222wusir111taibai')
      print(ret1)  # ['alex', 'wusir', 'taibai']
      print(ret2)  # ['alex', '222', 'wusir', '111', 'taibai']
      print(ret3)  # ['alex', '2', 'wusir', '1', 'taibai']
      
    2. sub()subn()

      全称为 substitute,和字符串的 replace 操作类似,可以设定替换的次数;而subn()可以返回一个元组,元组内分别为替换后的匹配字段和替换次数。

      ret1 = re.sub('d+', 'H', 'alex123wusir456')
      ret2 = re.sub('d+', 'H', 'alex123wusir456', 1)
      ret3 = re.subn('d+', 'H', 'alex123wusir456')
      print(ret1)  # alexHwusirH
      print(ret2)  # alexHwusir456
      print(ret3)  # ('alexHwusirH', 2)
      
    3. match()

      在作用上等价于search()方法的正则表达式参数前人为地添加了一个^;但是match()一般用来表达规定字段,而search()一般用来表达在字符串中寻找满足条件的子内容。

      ret1 = re.match('d+', 'eva123taibai456')  # ret1 = re.search('^d+', 'eva123taibai456')
      ret2 = re.match('d+', '123eva456taibai')
      print(ret1)  # None
      print(ret2)  # <_sre.SRE_Match object; span=(0, 3), match='123'>
      print(ret2.group())  # 123
      
    4. compile()

      一个合格的项目,不仅仅需要满足功能需要,也需要足够的性能;时间复杂度和空间复杂度就是一个程序性能好坏的主要指标,比如因为效率过低,list在底层是不适合使用insert()pop(n)操作的;回到 re 模块,加入一个正则表达式需要使用 多次,多次利用findall()research()操作同一正则表达式,会影响效率,故我们使用compile()操作,将读取正则表达式的操作集中在一起,可以提高程序的效率。

      ret = re.compile('d+')
      res1 = ret.search('alex37176')
      res2 = ret.findall('alex37176')
      print(res1)  # <_sre.SRE_Match object; span=(4, 9), match='37176'>
      print(res2)  # ['37176']
      
    5. finditer()

      complie()操作降低时间复杂度类似,finditer()会将寻找到的匹配字段用迭代器形式返回,可以极大的提升大文件匹配的空间性能,提升程序的效率。

      ret = re.finditer('d+', 'asdasdd312312')
      for i in ret:
          print(i.group())  # 312312
      
    6. 分组命名

      有时由于正则表达式过长,且我们需要的分组过多,使用数字索引会比较困难且易读性差,这时我们可以将分组以(?P<group_name>regrx)的形式命名,然后通过.group('group_name')的形式读取,增强程序的可读性,也便利了编程操作。

      ret = re.search('d(d)d(w+?)(d)(w)d(d)d(w+?)(d)(w)d(d)d(?P<name>w+?)(d)(w)',
                '123abc312321dasdsd1231233213123_213421sdddsa')
      print(ret.group(10))  # 一个一个数容易乱
      print(ret.group('name'))  # 一个一个数容易乱
      
    7. 分组命名的引用操作

      # 分组命名的引用
      # <tag_name>string</tag_name>写一个匹配标签的regex
      # 方法一,分组命名
      exp = '<abc>dasdas&**Dh</abc>as&**Dh</abd>'
      ret = re.search('<(?P<tag>w+)>.*?</(?P=tag)>', exp)
      print(ret.group())  # <abc>dasdas&**Dh</abc>
      
      # 方法二,1引用,因为1在Python中有特殊意义,所以需要取消1的特殊意义
      exp = '<abc>dasdas&**Dh</abc>as&**Dh</abd>'
      ret = re.search(r'<(w+)>.*?</1>', exp)  # 将字符串加raw,取消1的特殊含义
      print(ret.group())  # <abc>dasdas&**Dh</abc>
      

    三、递归函数

    递归函数就是在一个函数中调用函数本身。由于需要反复调用本身,就涉及到了递归深度的问题,在 Python 中,我们人为地规定了递归的深度为1000(也有写做998、997)。sys模块中的sys.setrecursionlimit(n)虽然可以修改递归的深度,但是由于递归需要反复调度,其本身的效率其实是较低的。但是递归传达了一种分治思想,可以把一个复杂的问题分解为一系列类似的简单问题解决。递归的逻辑具有两个重要概念:

    ​ ①递归边界

    ​ ②递归式(递归调用)

    现在我们利用一个阶乘的例子来简单理解以下递归函数:

    def factorial(i):
        if i == 1:  # 递归边界
            return 1
        return factorial(i-1) * i  # 递归调用
    

    中间传达的其实是下列思想:

    factorial(n) = factorial(n-1) * n

    factorial(n-1) = factorial(n-2) * (n-1)

    factorial(n-2) = factorial(n-3) * (n-2)

    ....

    factorial(2) = factorial(1) * 2

    factorial(1) = 1

    下面提供了一些题目,尝试用递归的思想解决。

    # 2.os模块:查看一个文件夹下的所有文件,这个文件夹下面还有文件夹,不能用walk
    import os
    def read_dir(dirname):
        if os.listdir(dirname) == []:
            return
        for i in os.listdir(dirname):
            print(i)
            if os.path.isdir(os.path.join(dirname, i)):
                read_dir(os.path.join(dirname, i))
    # read_dir('day20')
    
    # 3.计算一个文件夹下所有文件的大小,这个文件夹下面还有文件夹,不能用walk
    import os
    size = 0
    def read_dirsize(dirname):
        if os.listdir(dirname) == []:
            return
        for i in os.listdir(dirname):
            global size
            size += os.path.getsize(os.path.join(dirname, i))
            if os.path.isdir(os.path.join(dirname, i)):
                read_dirsize(os.path.join(dirname, i))
    # read_dirsize('day20')
    
    # 4.计算斐波那契数列
    def Fibonacci(n):
        if n == 1 or n == 0:
            return 1
        return Fibonacci(n-1) + Fibonacci(n-2)
    # print(Fibonacci(40))
    
    # 5.三级菜单
    menu = {
        '北京': {
            '海淀': {
                '五道口': {
                    'soho': {},
                    '网易': {},
                    'google': {}
                },
                '中关村': {
                    '爱奇艺': {},
                    '汽车之家': {},
                    'youku': {},
                },
                '上地': {
                    '百度': {},
                },
            },
            '昌平': {
                '沙河': {
                    '老男孩': {},
                    '北航': {},
                },
                '天通苑': {},
                '回龙观': {},
            },
            '朝阳': {},
            '东城': {},
        },
        '上海': {
            '闵行': {
                "人民广场": {
                    '炸鸡店': {}
                }
            },
            '闸北': {
                '火车战': {
                    '携程': {}
                }
            },
            '浦东': {},
        },
        '山东': {},
    }
    def read_menu(menu):
        if menu == {}:
            return
        while True:
            for i in menu:
                print(i)
            select = input('input>>')
            if select.upper() == 'Q':
                break
            elif select in menu:
                read_menu(menu[select])
            else:
                print('ILLEGAL INPUT')
    read_menu(menu)
    

    四、带参数的装饰器

    装饰器的思想之前已经介绍过了,主要是满足开放封闭原则;我们提前写好一个功能,让别人需要的时候就能够直接使用完成相应的功能,不需要逐行修改代码。这里有一个装饰器非常典型的功能:写日志,即在项目的日志文件中,记录在什么时间,调用了什么函数,完成了什么操作等等;以购物车功能为例,我们每个操作都希望能够写下日志,但是不同类型的日志我们希望能够记录到不同的日志文件中,这时我们不必写两个装饰器,只需要将装饰器再包裹一层,用来传输装饰器的参数,达到在不同文件中写日志的目的。

    # 登录和注册的信息 写道auth.log文件里
    # 所有的购物信息,写道operate.log文件里
    
    import time
    
    def log(path):
        def wrapper(func):
            def inner(*args, **kwargs):
                ret = func(*args, **kwargs)
                with open(path, mode='a', encoding='utf-8') as f:
                    msg = '%s 执行了%s'%(time.strftime('%Y/%m/%d %H:%M:%S'), func)
                    f.write('
    ' + msg)
                return ret
            return inner
        return wrapper
    
    # 装饰器不带参数相当于
    # login = log(login)
    # 装饰器带参数相当于
    # ret = log('auth.log')
    # login = ret(login)
    @log('auth.log')
    def login():
        print('login')
    
    @log('auth.log')
    def register():
        print('register')
    
    @log('operate.log')
    def show_goods():
        print('show goods')
    
    @log('operate.log')
    def add_goods():
        print('add goods')
    

    根据上面的例子,我们可知带参数的装饰器基本形式为:

    def decorator(args):
        def wrapper(func):
            def inner(*args, **kwargs):
                """
                在函数运行之前添加的额外功能
                """
                ret = func(*args, **kwargs)
                """
                在函数运行之后添加的额外功能
                """
                return ret
            return inner
        return wrapper
    
  • 相关阅读:
    线程笔记
    值类型与引用类型
    abstract抽象 抽象方法 不能有实现{} 0907
    接口
    结构
    XML初探
    javaScript中为什么会有变量提升?
    Windows 7实现自动登录(本地账户和域账户)
    WCF 提示 "由于正在读取的 XML 数据的嵌套级比配额所允许的多,因此已超出最大读取深度 (32)" 的解决办法
    也来安个家!
  • 原文地址:https://www.cnblogs.com/raygor/p/13333854.html
Copyright © 2020-2023  润新知