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('\n' + 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
posted @ 2020-07-17 22:21  Raigor  阅读(188)  评论(0编辑  收藏  举报