【Python】装饰器、正则表达式

装饰器

装饰器(Decorators)是 Python 中非常强大且灵活的工具,用于修改或增强函数和方法的行为。它们本质上是一个返回函数的高阶函数,可以用来在不改变原函数代码的情况下为其添加新功能。装饰器通常用于日志记录、访问控制、性能测量等场景。

装饰器的基本概念

  • 高阶函数:接受一个函数作为参数或者返回一个函数的函数。
  • 闭包:函数内部定义了另一个函数,并且这个内部函数引用了外部函数的局部变量,即使外部函数已经结束执行,内部函数仍然可以访问这些变量。
  • 装饰器结合了这两个概念,通过包装原始函数来扩展其行为,而不需要直接修改该函数的源代码。
  • 一、装饰器的理解

    1、闭包

    要想了解装饰器,首先要了解一个概念,闭包。什么是闭包,一句话说就是,在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了。

    2、什么是装饰器?

    其实装饰器就是一个闭包,装饰器是闭包的一种应用。什么是装饰器呢,简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。使用时,再需要的函数前加上@demo即可。

    3、为何要用装饰器

    开放封闭原则
    开放:指的是对拓展功能是开放的
    封闭:指的是对修改源代码是封闭的
    装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能

    二、案例一

    需求:给多个函数,增加一个输出函数执行时间的功能。

    import time
    from functools import wraps
    
    
    def timer(func):
        # @wraps(func)
        def inner():
            start_time = time.time()
            func()
            wait_time = time.time() - start_time
            print("%s 运行时间:" % func.__name__, wait_time)
    
        return inner
    
    @timer
    def test_1():
        time.sleep(1)
        print("test1")
    
    
    def test_2():
        time.sleep(2)
        print("test2")
    
    @timer
    def test_3():
        time.sleep(3)
        print("test3")
    
    test_3()
    print(test_3.__name__)
    

    三、案例二

    需求:编写装饰器,为多个函数加上认证功能(用户的账户密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码,直接可以执行。要求:文件名可以自定义。

    import os, time, sys
    from functools import wraps
    
    user_status = False
    
    
    def login(users_path):
        def login_decorator(func):
            if os.path.exists(users_path):
                with open(file=users_path, mode='r+', encoding='utf-8') as file:
                    line = file.read()
                    user_info = eval(line)
    
            else:
                with open(users_path, mode='w', encoding='utf-8') as file:
                    choice = input("是否注册用户?[Y/N]")
                    if choice == 'Y' or choice == 'y':
                        name = input("请输入新用户用户名:")
                        password = input("请输入新用户密码:")
                        user_info = {'name': name, 'password': password}
                        row = str(user_info)
                        file.write(row)
                    else:
                        print('没有用户信息,程序退出')
                        sys.exit(2)
    
            @wraps(func)
            def inner():
                _username = user_info['name']
                _password = user_info['password']
                global user_status
                if user_status is False:
                    username = input("输入用户名:")
                    password = input("密码:")
                    if username == _username and password == _password:
                        print(f"欢迎{username}登录...")
                        user_status = True
                    else:
                        print("用户名或者密码错误")
                if user_status:
                    func()
    
            return inner
    
        return login_decorator
    
    
    @login('users.txt')
    def test_1():
        time.sleep(1)
        print("test1")
    
    
    @login('users.txt')
    def test_2():
        time.sleep(2)
        print("test2")
    
    
    @login('users.txt')
    def test_3():
        time.sleep(3)
        print("test3")
    
    
    test_3()
    test_2()
    test_1()
    
    

    正则表达式

    Python 中的正则表达式(Regular Expressions, 简称 regex 或 regexp)是用于匹配字符串模式的强大工具。它们可以用来搜索、编辑或操作文本,执行复杂的字符串处理任务。Python 提供了一个内置模块 re 来支持正则表达式的使用。

    正则表达式的基本概念

  • 模式:正则表达式定义了一种“模式”,用于描述字符序列中的特定结构。
  • 匹配:当一个字符串符合给定的正则表达式模式时,我们说它与该模式“匹配”。
  • 元字符:正则表达式中有一些特殊的字符称为元字符,它们具有特殊含义,例如 .(匹配任意单个字符)、*(匹配前面的元素零次或多次)等。
  • Python 的 re 模块

    re 模块提供了多个函数和方法来处理正则表达式:

    常用函数

  • re.match(pattern, string):尝试从字符串的起始位置匹配一个模式,如果起始位置不匹配,则返回 None。
  • re.search(pattern, string):扫描整个字符串并返回第一个成功的匹配结果。
  • re.findall(pattern, string):返回所有非重叠匹配项的列表。
  • re.finditer(pattern, string):返回一个迭代器,产生所有非重叠匹配项的匹配对象。
  • re.sub(pattern, repl, string):替换字符串中所有匹配项为指定内容。
  • re.split(pattern, string):根据匹配项分割字符串。
  • 一、re模块中三个函数

    1、match函数

    re.match 尝试从字符串的起始位置匹配一个模式,匹配成功则返回的是一个匹配对象(这个对象包含了我们匹配的信息),如果不是起始位置匹配成功的话,match()返回的是空。

    注意:match只能匹配到一个

    s = 'python123python666python888'
    #s = '1python123python666python888'
    
    result = re.match('python', s)
    print(result)  # <re.Match object; span=(0, 6), match='python'>
    print(result.span())  # (0, 6)
    print(result.group())  # python
    
    
  • 通过span()提取匹配到的字符下标
  • 通过group()提取匹配到的内容
  • 2、search函数

    re.search 扫描整个字符串,匹配成功则返回的是一个匹配对象(这个对象包含了我们匹配的信息);当然,若是都找不到则返回None值。
    注意:search也只能匹配到一个,找到符合规则的就返回,不会一直往后找

    总结:

    查找并返回一个匹配项的函数有3个:search、match、fullmatch,他们的区别分别是:

    search: 查找任意位置的匹配项
    match: 必须从字符串开头匹配
    fullmatch: 整个字符串与正则完全匹配

    3、findall函数

    在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表

    s = '1python123python666python888'
    
    result = re.findall('python', s)
    print(result)  # ['python', 'python', 'python']
    
    

    总结

    1.查找一个匹配项

    查找并返回一个匹配项的函数有3个:search、match、fullmatch,他们的区别分别是:

    search: 查找任意位置的匹配项
    match: 必须从字符串开头匹配
    fullmatch: 整个字符串与正则完全匹配

    2.查找多个匹配项

    查找多项函数主要有:findall函数 与 finditer函数:

    findall: 从字符串任意位置查找,返回一个列表
    finditer:从字符串任意位置查找,返回一个迭代器

    正则表达式的应用方向:

  • 判断用户注册帐号是否满足格式
  • 抓取页面中特定部分数据
  • 判断用户提交的邮箱的格式是否正确 等等等等
    那么我们就要了解元字符
  • 二、正则表达式的案例

    1、案例一:匹配账号和密码

    账号要求:

    1.用户名只能包含数字 字母 下划线

    2.不能以数字开头

    3.⻓度在 6 到 16 位范围内

    import re
    
    s = 'xzq_2234w'
    
    result = re.match('^[a-z_][a-z\d_]{5,15}$', s)
    print(result)  # <re.Match object; span=(0, 10), match='1587022xzq'>
    
    

    因为账号是从头到尾的,只需要匹配一次就行,所以我们可以使用match方法

    密码要求:

    1.不能包含!@#¥%^&*这些特殊符号

    2.必须以字母开头

    3.⻓度在 6 到 12 位范围内

    s = 'xzq_2234w'
    result = re.match(r'[a-z][^!@#¥%^&*]{5,11}', s)
    print(result)
    

    2、案例二:匹配QQ号码

    要求: QQ号码的长度一般是5~10位, 全部都是数字,第一位不能是0

    s = '10086111222'
    # 5 - 11 位
    result = re.match(r'[1-9]\d{4,10}$', s)
    print(result)  # <re.Match object; span=(0, 11), match='10086111222'>
    
    

    3、案例三:检索所有的python源代码文件

    要求: python文件名是一个整体,包含字母,数字和下划线_ 。

    s = '1.py 2.png ssx.csv qaq.txt xzq.py'
    #文件名格式:  (数字字母_).py
    result = re.findall(r'\w+\.py\b', s)
    print(result)	# ['1.py', 'xzq.py']
    
    
    

    4、案例四:验证邮箱地址

    要求:邮箱地址验证,必须是网易邮箱或者是QQ邮箱。邮箱地址中必须有@,而且@符号前面至少5位,最多10位。

    # 验证输入的邮箱 163 126 qq 前面至少五位,至多11位
    email = '738473800@qq.com'
    result = re.match(r'\w{5,11}@(163|126|qq)\.(com|cn)$', email)
    print(result)  # <re.Match object; span=(0, 16), match='738473800@qq.com'>
    
    
    

    5、案例五:匹配座机号码

    要求:座机号码必须是:区号-号码,而且区号包含3或者4位,号码一定是8位

    phone = '010-12345678'
    result = re.match(r'(\d{3}|\d{4})-(\d{8})$', phone)
    print(result)  # <re.Match object; span=(0, 12), match='010-12345678'>
    print(result.group())  # 010-12345678
    print(result.group(1))  # 010
    print(result.group(2))  # 12345678
    
    

    6、案例六:匹配html标签中的内容

    要求:只提取合法标签中的内容,标签不要

    # 爬虫
    s = '<html>hello</h1>'
    s2 = '<html>hello</html>'
    result = re.match(r'<([0-9A-z]+)>(.+)</\1>$', s)
    print(result)  # None
    result = re.match(r'<([0-9A-z]+)>(.+)</\1>$', s2)
    print(result)  # <re.Match object; span=(0, 18), match='<html>hello</html>'>
    print(result.group(1))  # html
    print(result.group(2))  # hello
    
    
    分组取名
    # 取名字 (?P<名字>正则) (?P=名字)
    s = '<html>hello</h1>'
    s2 = '<html>hello</html>'
    result = re.match(r'<(?P<name1>\w+)>(.+)</(?P=name1)>$', s)
    print(result)  # None
    result = re.match(r'<(?P<name1>\w+)>(?P<msg>.+)</(?P=name1)>$', s2)
    print(result)  # <re.Match object; span=(0, 18), match='<html>hello</html>'>
    print(result.group('name1'))  # html
    print(result.group('msg'))  # hello
    
    
    

    7、案例七:提取数字并计算

    邀请:提取一个包含有数值的字符串中的数值 (数值包括正负数, 还包括整数和小数在内) 并求和。

    例如:“-3.14good87nice19bye” =====> -3.14 + 87 + 19 = 102.86

    import re
    from functools import reduce
    
    s = '-3.14good87nice19bye'
    nums = re.findall(r'-?\d+\.?\d*', s)
    result = reduce(lambda x, item: x + float(item), nums, 0)
    print(result)
    

    8、案例八:匹配整数或者小数

    要求: 匹配字符串是否,是整数或者小数,包含(正数或者负数,正数前面可以+,也可以没有)

    s = '-3.14'
    result = re.fullmatch(r'[+-]?(0|[1-9]\d*)(\.\d+)?', s)
    
    print(result)
    

    [0-9a-z] ==(0-9a-z)

    作者:道友老李

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【Python】装饰器、正则表达式

    发表回复