【Python】进阶 - 生成器、正则表达式

发布于:2025-07-04 ⋅ 阅读:(20) ⋅ 点赞:(0)
系列篇章🎉
No. 文章
1 【Python】基础知识(详细)🚀
2 【Python】基础 - 循环、容器类型🚀
3 【Python】基础 - 推导式、函数🚀
4 【Python】基础 - 文件、异常、模块🚀
5 【Python】进阶 - 面向对象(详细)🚀
6 【Python】进阶 - 闭包、装饰器、深浅拷贝🚀
7 【Python】进阶 - 网络编程(TCP开发基础、进程和线程)🚀

🌟文章目录

一、生成器

1.1 生成器推导式

 1.2 yield生成器

1.3 生成器优势

二、正则表达式

2.1 re模块常用函数和方法:

2.2 标识修饰符

2.3 正则表达式符号表

2.4 正则表达式的锚点:

2.5 正则表达式量词表

2.6 分组(子表达式)

2.7 捕获

2.8 非捕获分组

2.9 命名捕获组

3.0 反向引用

一、生成器

生成器(Generator)是 Python 中一种特殊的迭代器,它可以通过简单的语法创建惰性序列,按需逐个生成值。与普通函数不同的是,生成器不会一次性将所有结果计算出来并存储在内存中,而是每次只产生一个值,在需要时才进行计算,从而节省内存资源。生成器底层保存的并不是具体的数据,而是生成规则。

节省内存:不像列表那样一次性把所有数据加载到内存中。
惰性求值(Lazy Evaluation):只有在需要的时候才生成下一个值。
处理大数据流或无限序列:适合用于处理大文件、日志、网络流等场景。

1.1 生成器推导式

生成器本质上是一个可暂停和恢复执行的函数。每次遇到 yield 或者生成器表达式中的下一个值时,它会暂停并保存当前状态,下次调用时从中断处继续。

# 创建生成器
generator = (i for i in range(5))
# 生成器对象不支持索引操作,访问元素可以使用 next() 函数, 每次返回一个元素
print(next(generator))  # 0
print(next(generator))  # 1
# 可以利用列表一次性获取剩余所有值
list1 = list(generator)
print(list1)    # [2, 3, 4]

generator = (i for i in range(5))
for v in generator:
    print(v,end=' ')    # 0 1 2 3 4 
操作 效果
next(generator) 获取下一个值,若已无值则抛出 StopIteration
list(generator) 一次性获取剩余的所有值,并清空生成器
for 循环 遍历生成器中的每个元素,自动处理了停止迭代异常
while 循环 内部没有处理异常操作,需要手动添加处理异常操作
生成器不可重复使用 遍历结束后需要重新创建才能再次使用

 1.2 yield生成器

当函数中使用了 yield 后,这个函数就不再是普通的函数了,而是一个生成器函数。调用它不会立即执行函数体,而是返回一个生成器对象(generator object)。

yield 类似于 return,但它不会终止函数,而是“暂停”函数状态,它会“记住”当前状态(包括变量、执行位置等),下次调用时从上次暂停的地方继续执行。

def fenerator2(num):
    for i in range(num):
        print('开始生成...')
        yield i
        print('生成一次...')

fen = fenerator2(3)
print(next(fen))    # 0
print(next(fen))    # 1
# print(next(fen))    # 2

1.3 生成器优势

操作 是否立刻分配内存 内存是否显著增长 是否推荐用于大数据
列表推导式 [n for ...] ❌ 不适合大数据
生成器表达式 (n for ...) ✅ 推荐用于大数据
对生成器调用 list() ❌ 效果等同于列表推导式
import memory_profiler as mem

num = list(range(10000000))
# 监控函数或代码行的内存使用情况
print('运算前内存:',mem.memory_usage())  # 运算前内存: [458.328125]
# 列表
# list1 = [n*n for n in num]
# 生成器
list1 = (n*n for n in num)

print('运算后内存:',mem.memory_usage())  # 运算后内存: [458.3359375]

list2 = list(list1)

print('运算后内存:',mem.memory_usage())  # 运算后内存: [840.5546875]

二、正则表达式

正则表达式(Regular Expression,简称 RegEx) 是一种用于匹配字符串中特定模式的强大工具。它可以用来:

  • 检查一个字符串是否包含某个子串
  • 替换某些字符
  • 提取信息(如手机号、邮箱等)
  • 校验格式(如身份证号、电话号码)

在 Python 中,正则表达式通过标准库模块 re 来实现。

# 第一步:导入re模块
import re
# 第二步:使用match方法进行匹配操作
result = re.match(pattern正则表达式, string要匹配的字符串, flags=0)
# 第三步:如果数据匹配成功,使用group方法来提取数据
result.group()
参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志

 案例:

import re
res = re.search('hello', 'hello eyerybody')
print(res.group())  # hello

2.1 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) 按照正则表达式分割字符串

 简单举例:

import re
res = re.search('hello', 'hello eyerybody')
print(res.group())  # hello
r = re.search('2','123sdf52')
print(r.group())    #2
r = re.findall('2','123sdf52')
print(r)    # ['2', '2']
r = re.finditer('2','123sdf52')
print(r)    # <callable_iterator object at 0x000001E47DB47C70>
new_r = re.sub('sdf','*****','123sdf52')
print(new_r)    # 123*****52
new_r = re.split('-','1-3s-d-f52')
print(new_r)    # ['1', '3s', 'd', 'f52']

2.2 标识修饰符

在 Python 的 re 模块中,正则表达式可以使用标志修饰符(Flags)来控制匹配行为。这些标志是可选的参数,用于改变正则表达式的匹配方式。

多个标志可以通过按位或操作符 | 组合在一起,例如:re.I | re.M 表示同时启用忽略大小写和多行模式。

标志常量 含义说明 简写形式
re.I 忽略大小写(如 a 匹配 Aa (?i)
re.M 多行模式^$ 可以匹配每一行的开始和结束 (?m)
re.S . 匹配包括换行符在内的所有字符 (?s)
re.L 使用当前区域设置进行匹配(依赖系统环境,不推荐使用) (?L)
re.U 使用 Unicode 字符集进行匹配(默认已启用,Python 3) (?u)
re.X 允许写更易读的正则表达式,支持注释和空白符 (?x)

案例:

import re
print(re.search('hello', 'HELLO', re.I))  # 匹配成功

使用按位或运算符 | 来组合多个标志:

import re

text = "Start\nstart with lowercase"
matches = re.findall(r'^start', text, re.I | re.M)
print(matches)  # ['Start', 'start']

2.3 正则表达式符号表

正则符号 功能说明
.(英文点号) 匹配任意一个字符(除换行符 \n 以外)
[ ] 匹配括号中列出的任意一个字符(称为 字符簇字符集合
[^字符] 匹配除了指定字符以外的其他某个字符
\d 匹配任意一个数字字符(0-9)
\D 匹配任意一个非数字字符
\s 匹配任意一个空白字符(包括空格、制表符 \t、换行符 \n 等)
\S 匹配任意一个非空白字符
\w 匹配任意一个“单词字符”,即:a-z、A-Z、0-9 和下划线 _
\W 匹配任意一个“非单词字符”,即不匹配 \w 的字符

📌 术语补充说明:

  • 字符簇(Character Class)

    • 比如 [abc] 表示匹配 a、b、c 中任意一个字符。
    • 再比如 [a-z] 表示匹配所有小写字母。
  • 否定字符类 [^...]

    • [^aeiou] 表示匹配除了元音字母之外的任意一个字符。
    • 注意:^ 必须写在 [] 内的第一个位置才表示“否定”。
  • 关于 ^ :

    • 在正则中,^ 在不同上下文中含义不同:
      • [] 中开头表示“否定”
      • 在正则开头表示“字符串起始位置”

2.4 正则表达式的锚点:

^ 和 $ 是正则表达式中非常重要的锚点,它们不匹配实际字符,而是用于指定位置边界。这两个符号可以用来确保整个字符串符合某个格式,比如验证手机号、邮箱、身份证号等。

代码 功能
^ 匹配以某个字符串开头
$ 匹配以某个字符串结尾

  • ^$ 不会消耗字符,只是表示“位置”。
  • 在多行模式下(re.M),^$ 还会匹配每一行的开始和结束。
  • 如果想确保整个字符串都满足某种格式,需要使用 ^...$ 将整个表达式包裹起来。

举例:^abc 字符串必须以 "abc" 开头,xyz$ 字符串必须以 "xyz" 结尾,^abc$ 字符串必须完全等于 "abc"

2.5 正则表达式量词表

正则符号 功能说明 示例说明
* 匹配前一个字符出现 0 次或多次(即“可有可无”,匹配 0 到任意多) go*gle 可匹配 ggle, google, gooooogle
+ 匹配前一个字符出现 1 次或多次(至少 1 次,不能没有) go+gle 至少要有一个 o,如 google, gooogle,但不匹配 ggle
? 匹配前一个字符出现 0 次或 1 次(要么没有,要么只有一次) colou?r 可以匹配 colorcolour
{m} 匹配前一个字符 恰好出现 m 次 \d{11} 表示 11 位数字,如手机号码
{m,n} 匹配前一个字符 最少 m 次,最多 n 次 \w{6,10} 表示用户名长度在 6~10 个单词字符之间
{m,} 匹配前一个字符 至少 m 次,无上限 \d{5,} 表示至少 5 位数字

📌 术语解释:

  • 前一个字符:指的是量词前面的那个字符或者用括号括起来的整体。

    • ab+ 表示匹配 a 后面跟着至少一个 b
    • (abc)+ 表示匹配整个 abc 组合重复一次或多次
  • 贪婪 vs 懒惰模式

    • 默认情况下,量词是 贪婪的(Greedy),尽可能多地匹配
    • 加上 ? 就变成 懒惰的(Lazy),尽可能少地匹配
      • .* 贪婪匹配任意字符
      • .*? 懒惰匹配任意字符

懒惰举例:

reg = re.match(r'[abcd]{2,5}','cddsa')
print(reg.group(),'----------------------')

reg = re.match(r'[abcd]{2,5}?','cddsa')
print(reg.group(),'----------------------')

reg = re.match(r'[a-z]?','ckjgy1222a')
print(reg.group(),'==========')

reg = re.match(r'[a-z]??','ckjgy1222a')
print(reg.group(),'==========')

 

综合案例:

import re

rem = re.match('[a-z]','sSDo123456789aeAIF',re.I)
print(rem.group())  # s
rem = re.findall('[a-z]','sSDo12345s6789AIF',re.I)
print(''.join(rem))  # sSDosAIF
# '\\d'中第一个\标识转义
rem = re.findall('\\d','sSDo12345s6789AIF',re.I)
print(''.join(rem))  # 123456789
rem = re.findall('^A[^0-9]C$','A8C',re.I)
print(''.join(rem))  #
rem = re.findall('^A[^0-9]C$','ABC',re.I)
print(''.join(rem))  # ABC
# 以A开头,中间匹配0个或多个字符(不能为数字),以C结尾
rem = re.findall('^A[^0-9]*C$','ABCDEC',re.I)
print(''.join(rem))  # ABCDEC

2.6 分组(子表达式)

使用小括号 () 把一部分正则表达式组合在一起,形成一个整体。

先看效果:

import re
# 获取年、月、日信息
text = "2023-12-25"
match1 = re.match(r'(\d{4})-(\d{2})-(\d{2})', text)
print(match1.groups())   # ('2023', '12', '25')

🌟 亮点总结:

1️⃣ ():分组(Grouping) & 捕获(Capturing)
        把多个字符作为一个整体,可以捕获匹配内容供后续使用(如提取、替换),支持命名捕获组 (?P<name>...)

2️⃣[]:字符类(Character Class)

        匹配其中任意一个字符,支持范围表示法(如 a-z 、0-9), 使用 ^ 表示“非”(否定)

3️⃣ {}:量词(Quantifier)

        控制前面字符或分组的重复次数,支持精确次数、最小最大范围等

符号 类型 是否捕获 是否影响匹配逻辑 示例 含义
() 分组 ✅ 是(默认) ✅ 影响逻辑 (abc)+ abc 至少出现一次
[] 字符类 ❌ 否 ✅ 影响逻辑 [abc] 匹配 a、b、c 中任一字符
{} 量词 ❌ 否 ✅ 影响逻辑 \d{3} 匹配 3 位数字

2.7 捕获

将某个分组匹配到的内容保存下来,供后续使用(比如提取、替换时使用)。

import re
# 获取年、月、日信息
text = "2023-12-25"
match1 = re.match(r'(\d{4})-(\d{2})-(\d{2})', text)
# 可以通过 .group(1)、.group(2) 来获取每组的匹配内容
print(match1.group(1))   # 2023
print(match1.group(2))   # 12
print(match1.group(3))   # 25

2.8 非捕获分组

只对正则做逻辑分组,但不希望它被捕获,可以用 (?:...)

import re
text = "http://example.com or https://test.com"
matches = re.match(r'(?:http|https)://\w+\.\w+', text)
print(matches.groups()) # ()

2.9 命名捕获组

代码 功能
(?P<name>) 分组起别名
(?P=name) 引用别名为name分组匹配到的字符串
import re
text = "2023-12-25"
match = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', text)

print("年:", match.group('year'))   # 年: 2023
print("月:", match.group('month'))  # 月: 12
print("日:", match.group('day'))    # 日: 25

3.0 反向引用

反向引用指的是:在正则中引用前面已经捕获的内容。

语法:

  • \1 表示引用第一个捕获组的内容
  • \2 表示第二个捕获组
  • (?P=name) 表示引用命名组的内容
text = "hello hello world"
match = re.search(r'(\w+)\s+\1', text)
print(match.group())  # 输出: hello hello

解释:

    (\w+) 捕获第一个单词
    \s+ 匹配空格
    \1 表示再次匹配和第一个相同的词

连续的4个数字,但是数字的格式为1111、2222、3333、4444、5555效果?

re.search(r'(\d)\1\1\1', str1)

网站公告

今日签到

点亮在社区的每一天
去签到