系列篇章🎉
No. | 文章 |
---|---|
1 | 【Python】基础知识(详细)🚀 |
2 | 【Python】基础 - 循环、容器类型🚀 |
3 | 【Python】基础 - 推导式、函数🚀 |
4 | 【Python】基础 - 文件、异常、模块🚀 |
5 | 【Python】进阶 - 面向对象(详细)🚀 |
6 | 【Python】进阶 - 闭包、装饰器、深浅拷贝🚀 |
7 | 【Python】进阶 - 网络编程(TCP开发基础、进程和线程)🚀 |
🌟文章目录
一、生成器
生成器(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 匹配 A 和 a ) |
(?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 可以匹配 color 和 colour |
{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)