AI大模型应用之Python系列-Python基础知识(二)

132 阅读36分钟

1. Python的数据类型

1.1 基本数据类型

1.1.1 整数(Integer)-正数、负数或零:

# 基本整数运算
age = 18          # 定义一个表示年龄的整数
temperature = -5  # 定义一个表示温度的负数
count = 0         # 定义一个表示计数的零

# 大数字的可读性处理
population = 1_000_000_000  # 使用下划线分隔,提高可读性
print(f"世界人口约为:{population}人")  # 输出:世界人口约为:1000000000人

# 不同进制的表示
binary = 0b1010   # 0b开头表示二进制数,等于十进制的10
hex_num = 0xFF    # 0x开头表示十六进制数,等于十进制的255
print(f"二进制1010等于十进制:{binary}")  # 输出:二进制1010等于十进制:10

1.1.2 浮点数(Float)-带小数点的数字,比如温度、身高、体重等:

# 日常生活中的浮点数
height = 1.75     # 身高1.75米
weight = 65.5     # 体重65.5公斤
temperature = 36.5  # 体温36.5度

# 科学计数法表示
light_speed = 3e8  # 光速:3 × 10^8 米/秒
atom_size = 1e-10  # 原子大小:1 × 10^-10 米

# 浮点数运算
pi = 3.14159
radius = 5
area = pi * radius * radius  # 计算圆的面积
print(f"圆的面积是:{area}")  # 输出:圆的面积是:78.53975

1.1.3 布尔值(Boolean)-True和False:

# 基本布尔值
is_sunny = True    # 今天天气晴朗
is_raining = False # 今天没有下雨

# 比较运算
age = 18
can_vote = age >= 18  # 判断是否达到投票年龄
print(f"可以投票吗?{can_vote}")  # 输出:可以投票吗?True

# 逻辑运算
# 逻辑运算符:and、or、not
# 逻辑运算符用于组合多个条件
# and:所有条件都为True时,结果为True
# or:只要有一个条件为True,结果为True
# not:对条件取反

has_ticket = True
has_id = True
can_enter = has_ticket and has_id  # 需要同时满足两个条件
print(f"可以入场吗?{can_enter}")  # 输出:可以入场吗?True

1.1.4 复数(complex)

a = complex(3, 4)  # 等同于 3 + 4j
b = 1 + 2j
print(a)  # (3+4j)

# 可以通过 .real 获取实部,通过 .imag 获取虚部系数:
print(a.real)  # 输出 3.0(实部,返回浮点数)
print(a.imag)  # 输出 4.0(虚部系数,返回浮点数)

# 复数支持加减乘除等基本运算:
print(a + b)  # 输出 (4+6j)
print(a * b)  # 输出 (-5+10j)

# 使用 abs() 可以获取复数的模(即到原点的距离):
print(abs(3 + 4j))  # 输出 5.0(计算 sqrt(3² + 4²))

1.2 序列类型

1.2.1 字符串(Str)-字符串就是文字,可以是中文、英文或任何其他文字:

# 基本字符串
name = "小明"     # 使用双引号
greeting = '你好'  # 使用单引号
print(f"{name}说:{greeting}")  # 输出:小明说:你好

# 特殊字符处理
# 使用转义字符 \ 来表示特殊字符
message = "他说:\"Python很有趣!\""  # 使用转义字符
print(message)  # 输出:他说:"Python很有趣!"

# 三引号字符串
#可以包含多行文本,保留所有的换行和空格
#适合编写文档字符串、多行文本等

long_text = """这是一个多行字符串,
可以直接包含换行符,
不需要使用特殊的转义字符。"""
print(long_text)

1.2.2 列表(list)

用方括号 [] 表示,元素可修改,如 [1, 2, 'a'] 列表是可变对象,可以随时增删改查

# 1.列表的创建与基本操作

# 创建一个装水果的列表
fruits = ["苹果", "香蕉", "橙子"]
print(fruits)  # 输出整个列表:['苹果', '香蕉', '橙子']

# 查看列表中有多少个元素
print(len(fruits))  # 输出:3
# 2.通过索引访问元素
# 创建一个装水果的列表
fruits = ["苹果", "香蕉", "橙子"]

# 索引从0开始,依次访问每个水果
print(fruits[0])  # 输出第一个元素:苹果
print(fruits[1])  # 输出第二个元素:香蕉
print(fruits[2])  # 输出第三个元素:橙子

# 访问最后一个元素,可以用-1
print(fruits[-1])  # 输出最后一个元素:橙子
# 访问倒数第二个元素
print(fruits[-2])  # 输出倒数第二个元素:香蕉
# 访问不存在索引的元素会报错
# print(fruits[3])  # 报错:IndexError: list index out of range
# 3. 添加和插入元素
# 创建一个装水果的列表
fruits = ["苹果", "香蕉", "橙子"]
# 在列表末尾添加一个新水果
fruits.append("葡萄")
print(fruits)  # 输出:['苹果', '香蕉', '橙子', '葡萄']
# append一次只能添加一个元素
fruits.append("西瓜", "柠檬")  # 报错:TypeError: append() takes exactly one argument (2 given)

# 在指定位置插入一个水果,比如插到第2个位置(索引为1)
fruits.insert(1, "西瓜")
print(fruits)  # 输出:['苹果', '西瓜', '香蕉', '橙子', '葡萄']
# 4. 删除元素
# 创建一个装水果的列表
fruits = ["苹果", "香蕉", "橙子"]
# 删除最后一个水果
last = fruits.pop()
print(last)    # 输出被删除的水果:橙子
print(fruits)  # 输出:['苹果', '香蕉']

# 删除指定位置的水果,比如删除第2个(索引为1)
removed = fruits.pop(1)
print(removed) # 输出被删除的水果:香蕉
print(fruits)  # 输出:['苹果']
# 5. 修改元素
# 创建一个装水果的列表
fruits = ["苹果", "香蕉", "橙子"]
# 把第二个水果改成“柠檬”
fruits[1] = "柠檬"
print(fruits)  # 输出:['苹果', '柠檬', '橙子']
# 6. 列表可以包含不同类型的数据
# 列表中可以放数字、字符串、布尔值等
mixed = [100, "hello", False]
print(mixed)  # 输出:[100, 'hello', False]
# 7. 列表可以嵌套列表
# 创建一个嵌套列表,类似二维表格
matrix = [
    [1, 2, 3],
    [4, 5, 6]
]
# 访问第二行第三列的元素
print(matrix[1][2])  # 输出:6
# 8. 空列表
# 创建一个空列表
empty = []
print(len(empty))  # 输出:0

1.2.3 元组(tuple)

用圆括号 () 表示,元素不可修改,如 (1, 2, 'a')

# 1. 元组的创建与访问
# 创建一个元组,存放三种颜色
colors = ("红色", "绿色", "蓝色")
print(colors)  # 输出:('红色', '绿色', '蓝色')

# 访问元组的元素,和列表一样
print(colors[0])   # 输出:红色
print(colors[-1])  # 输出:蓝色
# 2. 元组的不可变性
# 创建一个元组,存放三种颜色
colors = ("红色", "绿色", "蓝色")
# 尝试修改元组的内容会报错
colors[1] = "黄色"  # 这一行会报错:TypeError: 'tuple' object does not support item assignment
# 元组中的列表内容可以修改
fruits = ("苹果", ["香蕉", "橙子"])
fruits[1][0] = "西瓜"
print(fruits)  # 输出:('苹果', ['西瓜', '橙子'])
# 3. 只有一个元素的元组写法
# 注意:只有一个元素的元组要加逗号
single = (42,)
print(type(single))  # 输出:<class 'tuple'>

not_tuple = (42)
print(type(not_tuple))  # 输出:<class 'int'>
# 4. 元组可以包含可变对象
# 元组中可以包含列表,列表的内容可以变
combo = ("A", [1, 2])
combo[1][0] = 99  # 修改元组中列表的内容
print(combo)  # 输出:('A', [99, 2])
# 5. 取出嵌套列表中的指定元素
# 这是一个三层嵌套的列表
data = [
    ["Tom", "Jerry", "Spike"],
    ["Python", "Java", "C++"],
    ["Alice", "Bob", "Eve"]
]

# 取出"Tom"
print(data[0][0])  # 输出:Tom
# 取出"Java"
print(data[1][1])  # 输出:Java
# 取出"Eve"
print(data[2][2])  # 输出:Eve
# 6. 判断变量类型
# 判断变量是不是元组
a = ()
b = (1)
c = [2]
d = (3,)
e = (4, 5, 6)

print(isinstance(a, tuple))  # True,空元组
print(isinstance(b, tuple))  # False,b是整数
print(isinstance(c, tuple))  # False,c是列表
print(isinstance(d, tuple))  # True,只有一个元素的元组
print(isinstance(e, tuple))  # True,多个元素的元组

1.2.4 范围(range)

表示整数序列,如 range(10) 生成 0-9 的整数

1.3 映射类型

1.3.1 字典(dict)

用花括号 {} 表示,由键值对组成,如 {'name': 'Alice', 'age': 20}

# 1. 字典的创建与基本用法
# 创建一个字典,存储学生的分数
scores = {
    "小明": 92,
    "小红": 88,
    "小刚": 75
}
print(scores)  # 输出整个字典:{'小明': 92, '小红': 88, '小刚': 75}

# 通过名字查找分数
print(scores["小明"])  # 输出:92
# 2. 添加和修改字典中的数据
# 创建一个字典,存储学生的分数
scores = {
    "小明": 92,
    "小红": 88,
    "小刚": 75
}
# 添加一个新同学的分数
scores["小丽"] = 81
print(scores)  # 输出:{'小明': 92, '小红': 88, '小刚': 75, '小丽': 81}

# 修改已有同学的分数
scores["小红"] = 90
print(scores)  # 输出:{'小明': 92, '小红': 90, '小刚': 75, '小丽': 81}
# 3. 判断key是否存在
# 创建一个字典,存储学生的分数
scores = {
    "小明": 92,
    "小红": 88,
    "小刚": 75
}
# 检查"小刚"是否在字典中
print("小刚" in scores)  # 输出:True

# 检查"小强"是否在字典中
print("小强" in scores)  # 输出:False
# 4. 安全地获取字典中的值
# 创建一个字典,存储学生的分数
scores = {
    "小明": 92,
    "小红": 88,
    "小刚": 75
}
# 直接用[]查找不存在的key会报错
# print(scores["小强"])  # 这一行会报 KeyError: '小强'

# 推荐用get方法,查不到时返回None或自定义的默认值
print(scores.get("小强"))        # 输出:None
print(scores.get("小强", 0))    # 输出:0
# 5. 删除字典中的数据
# 创建一个字典,存储学生的分数
scores = {
    "小明": 92,
    "小红": 88,
    "小刚": 75
}
# 删除"小刚"的分数
removed_score = scores.pop("小刚")  # pop会返回被删除的值
print(removed_score)  # 输出:75
print(scores)         # 输出:{'小明': 92, '小红': 88}
# 6. 字典的key必须是不可变对象
# 字典的key可以是字符串、数字、元组等不可变类型
info = {
    1001: "张三",
    (1, 2): "坐标点"
}
print(info) # {1001: '张三', (1, 2): '坐标点'}
# 不能用列表作为key,否则会报错
wrong = {[1, 2, 3]: "错误"}  # 这一行会报TypeError: unhashable type: 'list'
# 综合案例:学生信息表
# 创建一个学生信息表,key是学号,value是学生姓名
students = {
    "202301": "王伟",
    "202302": "李娜",
    "202303": "赵强"
}

# 查询学号为"202302"的学生姓名
print(students.get("202302", "查无此人"))  # 输出:李娜

# 删除学号为"202301"的学生
students.pop("202301")
print(students)  # 输出:{'202302': '李娜', '202303': '赵强'}
1.3.1.1 字典和列表的区别
  • 字典通过key查找value,速度非常快,适合做查找表。
  • 列表只能通过下标查找,数据越多查找越慢。
  • 字典占用内存较多,列表更节省空间。
  • 字典的key必须是不可变类型,列表的元素类型没有限制。
1.3.1.2 总结
  • 字典(dict)是一种通过key快速查找value的数据结构。
  • 可以随时添加、修改、删除数据。
  • key必须是不可变类型,value可以是任意类型。
  • 字典在Python中非常常用,适合做各种查找表和映射关系。

1.4 集合类型

1.4.1 集合(set)

用花括号 {} 表示,元素唯一且无序,如 {1, 2, 3};集合(set)是一种无序、无重复元素的数据结构。你可以把它想象成一个“去重的盒子”,盒子里每个元素都独一无二,顺序不重要。

# 1. 集合的创建方法
# 直接用大括号创建集合
numbers = {1, 2, 3}
print(numbers)  # 输出:{1, 2, 3}

# 用set()函数把列表转换为集合
fruits = set(["苹果", "香蕉", "苹果", "橙子"])
print(fruits)  # 输出:{'香蕉', '苹果', '橙子'},自动去重
# 2. 集合的去重特性
# 集合会自动去掉重复的元素
colors = {"红", "绿", "蓝", "红", "蓝"}
print(colors)  # 输出:{'红', '绿', '蓝'}
# 3. 添加和删除元素
# 创建一个集合
pets = {"猫", "狗"}
# 添加新元素
pets.add("兔子")
print(pets)  # 输出:{'猫', '狗', '兔子'}

# 再次添加已存在的元素不会报错,也不会重复
pets.add("狗")
print(pets)  # 输出:{'猫', '狗', '兔子'}

# 删除元素
pets.remove("猫")
print(pets)  # 输出:{'狗', '兔子'}
# 4. 集合的数学操作
# 创建两个集合
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# 交集:取两个集合都包含的元素
print(a & b)  # 输出:{3, 4}

# 并集:取两个集合所有不重复的元素
print(a | b)  # 输出:{1, 2, 3, 4, 5, 6}
# 5. 集合的元素类型要求
# 集合的元素必须是不可变类型,比如数字、字符串、元组
valid_set = {1, "hello", (2, 3)}
print(valid_set)  # 输出:{1, 'hello', (2, 3)}

# 不能把列表放进集合,否则会报错
wrong_set = {[1, 2, 3]}  # 这一行会报TypeError: unhashable type: 'list'
# 6. 不可变对象的理解
# 字符串是不可变对象,调用replace不会改变原字符串
text = "hello"
new_text = text.replace("h", "H")
print(text)      # 输出:hello(原字符串没变)
print(new_text)  # 输出:Hello(新字符串)

# 列表是可变对象,调用sort会改变原列表
nums = [3, 1, 2]
nums.sort()
print(nums)  # 输出:[1, 2, 3]
# 综合案例:去除重复元素
# 有一个带重复元素的列表
raw_data = ["A", "B", "A", "C", "B", "D"]
# 用set去重
unique_data = set(raw_data)
print(unique_data)  # 输出:{'A', 'B', 'C', 'D'}
1.4.1.1 总结
  • 集合(set)是一种无序、无重复元素的数据结构。
  • 可以用add添加元素,用remove删除元素。
  • 支持交集、并集等数学操作。
  • 集合的元素必须是不可变类型。
  • 集合常用于去重、数学集合运算等场景。

1.4.2 冻结集合(frozenset)

不可修改的集合,如 frozenset({1, 2, 3})

1.5 其他类型

1.5.1 空值(None):

# 空值的使用
student_name = None  # 表示学生姓名暂时未知
print(f"学生姓名:{student_name}")  # 输出:学生姓名:None

1.5.2 变量(数据的容器)-变量就像是一个标签,可以贴在任何数据上:

# 变量的基本使用
score = 95        # 给变量score贴上95这个数字
name = "小红"     # 给变量name贴上"小红"这个文字
is_passed = True  # 给变量is_passed贴上True这个布尔值

# 变量的重新赋值
score = 98        # 把score的标签从95换成了98
print(f"{name}的分数是:{score}")  # 输出:小红的分数是:98

# 弱类型
# Python是弱类型语言,变量不需要声明类型
# 变量可以随时赋值为不同类型的数据

# 变量的类型
# 使用type()函数查看变量的类型
print(type(score))  # 输出:<class 'int'>
print(type(name))   # 输出:<class 'str'>
print(type(is_passed))  # 输出:<class 'bool'>

# 多个变量同时赋值
x, y, z = 1, 2, 3
print(f"x={x}, y={y}, z={z}")  # 输出:x=1, y=2, z=3

# 变量的交换
a = 1
b = 2
a, b = b, a  # 交换a和b的值
print(f"a={a}, b={b}")  # 输出:a=2, b=1

案例:创建一个简单的学生信息卡

# 创建一个学生信息卡
student_name = "小明"
student_age = 15
student_height = 1.75
student_score = 95.5
is_excellent = student_score >= 90

# 使用f-字符串格式化输出
print(f"""
学生信息卡
----------
姓名:{student_name}
年龄:{student_age}岁
身高:{student_height}米
分数:{student_score}分
是否优秀:{'是' if is_excellent else '否'}
""")

1.6 查看变量的数据类型

使用 type() 函数查看变量的数据类型

x = 10
print(type(x))  # 输出 <class 'int'>

y = [1, 2, 3]
print(type(y))  # 输出 <class 'list'>

2.字符串编码

2.1 什么是字符串?

在Python中,字符串就是一串字符,比如你的名字、地址、喜欢的电影名等。字符串可以用单引号'、双引号",甚至三引号'''"""括起来。

# 用单引号定义字符串
nickname = '小明'
# 用双引号定义字符串
city = "北京"
# 用三引号定义多行字符串
poem = """春眠不觉晓,
处处闻啼鸟。"""
print(nickname)  # 输出:小明
print(city)      # 输出:北京
print(poem)      # 输出多行诗句

2.2 字符串的拼接与重复

你可以把多个字符串合成一个,也可以让字符串重复多次。

# 字符串拼接
first_name = "张"
last_name = "三"
full_name = first_name + last_name  # 用+号拼接
print(full_name)  # 输出:张三

## 字符串和数字的拼接
# 字符串和数字拼接时,不能直接拼接,需要先将数字转换为字符串
age = 20
message = "我今年" + str(age) + "岁"
print(message)  # 输出:我今年20岁

# 字符串重复
laugh = "哈" * 5  # 字符串可以和数字相乘,表示重复
print(laugh)  # 输出:哈哈哈哈哈

2.3 字符串的索引和切片

字符串其实是一个字符的序列,每个字符都有编号(从0开始),可以像操作列表一样操作字符串。

# 索引
word = "Python"
print(word[0])  # 输出第一个字符:P
print(word[-1]) # 输出最后一个字符:n

# 切片
greeting = "Hello, world!"
print(greeting[0:5])   # 输出:Hello(从第0到第4个字符,不包括第5个)
print(greeting[7:])    # 输出:world!(从第7个字符到结尾)
print(greeting[:5])    # 输出:Hello(从开头到第4个字符)

2.4 字符串常用方法

Python为字符串提供了很多实用的方法,比如大小写转换、查找、替换等。

# 大小写转换
text = "Python is Fun!"
print(text.lower())  # 全部转为小写:python is fun!
print(text.upper())  # 全部转为大写:PYTHON IS FUN!

# 查找和替换
sentence = "I love apples, apples are sweet."
print(sentence.find("apples"))  # 查找'apples'第一次出现的位置,输出:7
print(sentence.replace("apples", "oranges"))  # 替换所有'apples'为'oranges'

2.5 字符串格式化

有时候我们需要把变量的值插入到字符串中,Python有多种方式实现。

2.5.1 百分号(%)格式化

# 百分号格式化
name = "小红"
age = 20
# 这是Python中的百分号格式化字符串方法
# %s 是一个占位符,表示将被字符串替换
# %d 是一个占位符,表示将被整数替换
# 括号中的(name, age)是要插入到字符串中的变量
# 变量会按顺序替换占位符:name替换%s,age替换%d
print("大家好,我叫%s,今年%d岁。" % (name, age))  # %s表示字符串,%d表示整数

# 常见的格式化占位符:
# %s - 字符串
# %d - 整数
# %f - 浮点数
# %.2f - 保留两位小数的浮点数
# %% - 输出百分号本身

2.5.2 format方法

# format方法
fruit = "香蕉"
price = 3.5
print("今天的{}价格是{}元一斤。".format(fruit, price))  # 用{}占位,后面用format填充

2.5.3 f-string(推荐,Python 3.6及以上)

# f-string格式化
score = 98
student = "李雷"
print(f"{student}的考试成绩是{score}分。")  # 变量名直接写在{}里

2.6 转义字符

有些字符在字符串里有特殊含义,比如换行、制表符、引号等,需要用“\”来转义。

# 换行和制表符
info = "姓名:小明\n性别:男\t年龄:18"
print(info)
# 输出:
# 姓名:小明
# 性别:男	年龄:18

# 字符串中包含引号
quote = "他说:\"Python真有趣!\""
print(quote)  # 输出:他说:"Python真有趣!"

2.7 字符串与编码

2.7.1 字符编码的概念

计算机只能处理数字,字符串在存储和传输时需要转换为数字(字节)。常见的编码有ASCII、UTF-8、GBK等。

2.7.2 编码与解码

  • 编码(encode) :把字符串转成字节
  • 解码(decode) :把字节转回字符串
# 编码为UTF-8字节
msg = "你好,世界"
msg_bytes = msg.encode("utf-8")  # 编码为字节
print(msg_bytes)  # 输出:b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c'

# 解码回字符串
msg_str = msg_bytes.decode("utf-8")  # 解码为字符串
print(msg_str)  # 输出:你好,世界

2.7.3 编码错误的常见情况

在Python中,当程序执行过程中遇到错误时,会抛出异常。我们可以使用try-except语法来捕获并处理这些异常,避免程序崩溃。

# 错误示例:用GBK解码UTF-8字节
try:
    msg = "你好,世界"
    msg_bytes = msg.encode("utf-8")  # 编码为字节
    wrong_str = msg_bytes.decode("gbk")  # 这里会报错,因为尝试用GBK解码UTF-8编码的字节
except UnicodeDecodeError as e:
    # except 代码块:捕获并处理特定类型的异常
    # UnicodeDecodeError 是解码错误的异常类型
    # as e 将异常对象赋值给变量e,以便后续使用
    print("解码出错:", e)  # 打印错误信息

2.8 字符串遍历与判断

你可以像遍历列表一样遍历字符串的每个字符,还可以判断字符串内容。

# 遍历字符串
for char in "Python":
    print(char)  # 依次输出每个字符

# 判断字符串内容
email = "user@example.com"
print(email.startswith("user"))  # 判断是否以"user"开头,输出:True
print(email.endswith(".com"))    # 判断是否以".com"结尾,输出:True
print(email.isdigit())           # 判断是否全是数字,输出:False

2.9 lower

str.lower() 是一个字符串方法,用于将字符串中的所有大写字母转换为小写字母
它可以通过两种方式调用:

  1. 作为实例方法:字符串实例.lower()
  2. 作为类方法:str.lower(字符串实例)
# 示例1:作为实例方法
s1 = "HELLO WORLD"
print(s1.lower())  # 输出: "hello world"

# 示例2:作为类方法
s2 = "PYTHON编程"
print(str.lower(s2))  # 输出: "python编程"

# 注意:str.lower()只会转换ASCII字符中的大写字母,不会影响非字母字符或非ASCII字符
s3 = "ABC123!@#中文"
print(s3.lower())  # 输出: "abc123!@#中文"

# 在列表生成式中使用str.lower()和实例方法.lower()效果相同
# 但使用实例方法更为常见和直观

2.10 常见问题

  • 字符串不可变,修改字符串会生成新对象
  • 中文字符串建议统一使用UTF-8编码
  • 处理文件时要注意文件的编码格式
# 字符串不可变的例子
s = "hello"
s2 = s.replace("h", "H")
print(s)   # 输出:hello(原字符串没变)
print(s2)  # 输出:Hello(新字符串)

3.if条件判断

# 1. 基本用法
# 假设我们要判断一个人的年龄是否成年
age = 21  # 定义一个变量age,表示年龄

if age >= 18:  # 如果年龄大于等于18
    print("你已经成年啦!")  # 满足条件时执行
    print("可以独立做很多事情。")  # 这行也会被执行
# 如果age小于18,这两行不会被执行
# 2. if...else:有选择地执行
# 判断一个人是否可以考驾照
age = 15

if age >= 18:
    print("你可以考驾照了!")  # 如果年龄大于等于18,执行这里
else:
    print("你还未成年,不能考驾照。")  # 否则执行这里
# 3. if...elif...else:多种情况分支
# 判断考试成绩等级
score = 72

if score >= 90:
    print("优秀")
elif score >= 75:
    print("良好")
elif score >= 60:
    print("及格")
else:
    print("不及格")
# 程序会从上往下判断,遇到第一个满足条件的分支就执行,然后跳过后面的分支
# 4. 条件判断的顺序很重要
# 看看下面的例子
temperature = 30

if temperature > 0:
    print("天气温暖")
elif temperature > 20:
    print("天气炎热")
else:
    print("天气寒冷")
# 结果会输出"天气温暖",因为第一个条件已经满足,后面的elif不会再判断
# 5. 条件可以是任意表达式
# 判断一个字符串是否为空
name = ""

if name:  # 空字符串会被当作False
    print("名字不为空")
else:
    print("名字是空的")
    
    
# 判断一个列表是否有内容
numbers = [1, 2, 3]

if numbers:  # 非空列表会被当作True
    print("列表有内容")
else:
    print("列表是空的")

numbers2 = []

if numbers2:  # 空列表会返回False
    print("列表有内容")
else:
    print("列表是空的")    
# 6. input输入与类型转换
# input()得到的内容类型是字符串
user_input = input("请输入你的年龄:")
print(type(user_input))  # 输出:<class 'str'>

user_input = input("请输入你的年龄:")
# 要和数字比较时需要用int()转换类型
age = int(user_input)
if age >= 18:
    print("你已经成年啦!")
else:
    print("你还未成年,不能考驾照。")

# 如果直接拿字符串和数字比较,会报错
# TypeError: '>=' not supported between instances of 'str' and 'int'
age = input("请输入你的年龄:")
if age >= 18:
    print("你已经成年啦!")
else:
    print("你还未成年,不能考驾照。")
# 7. 异常情况:输入不是数字怎么办?
# 如果用户输入的不是数字,int()会报错
user_input = input("请输入一个数字:")
try:
    number = int(user_input)  # 尝试转换为整数
    print("你输入的数字是:", number)
except ValueError:
    print("输入的内容不是有效的数字,请重新输入。")
    
# 综合案例:BMI健康指数判断
# 小明的身高和体重
height = 1.75  # 单位:米
weight = 80.5  # 单位:千克

# 计算BMI指数,公式:体重 / (身高的平方)
bmi = weight / (height ** 2)
print(f"小明的BMI指数为:{bmi:.2f}")  # 保留两位小数

# 根据BMI指数判断健康状况
if bmi < 18.5:
    print("体重过轻")
elif bmi < 25:
    print("体重正常")
elif bmi < 28:
    print("体重偏重")
elif bmi < 32:
    print("肥胖")
else:
    print("严重肥胖")

4. match 语句

在实际编程中,我们经常需要根据变量的不同取值执行不同的操作。传统的做法是用一连串的 if...elif...else 语句,但这样代码会变得很长、很难读。Python 3.10 及以上版本引入了 match 语句,让多分支判断变得更清晰、更优雅。

# 1. match 语句的基本用法
# 假设我们要根据天气情况给出不同的建议
weather = "rainy"
# 使用match语句进行多分支判断
match weather:
    case "sunny":
        print("天气晴朗,适合出门散步。")  # 如果是晴天
    case "rainy":
        print("下雨了,记得带伞。")        # 如果是雨天
    case "snowy":
        print("下雪了,注意保暖。")        # 如果是雪天
    case _:
        print("无法识别的天气类型。")      # 其他情况
# 2. 匹配多个值和范围
# 判断一个月份属于哪个季节
month = 4
match month:
    case 12 | 1 | 2:# 12月、1月、2月属于冬季
        print("冬季")
    case 3 | 4 | 5:# 3月、4月、5月属于春季
        print("春季")
    case 6 | 7 | 8:# 6月、7月、8月属于夏季
        print("夏季")
    case 9 | 10 | 11:# 9月、10月、11月属于秋季  
        print("秋季")
    case _:# 其他情况
        print("月份无效")
# 3. 带条件的匹配(守卫)
# 判断分数等级
score = 85

match score:
    case s if s >= 90:
        print("成绩优秀")
    case s if 80 <= s < 90:
        print("成绩良好")
    case s if 60 <= s < 80:
        print("成绩及格")
    case _:
        print("成绩不及格")
# 4. 匹配列表和解包
# 假设有一个命令行参数列表
command = ["run", "main.py", "--debug"]

match command:
    case ["run"]:
        print("缺少要运行的文件名。")
    case ["run", filename]:
        print(f"正在运行文件:{filename}")
    case ["run", filename, *options]:
        print(f"运行{filename},附加参数:{options}")
    case ["help"]:
        print("显示帮助信息。")
    case _:
        print("未知命令。")
        
# `*options`表示匹配剩下的所有参数,放到一个列表里。
# `case _:`用来处理所有未被前面分支匹配到的情况。

5. 循环

5.1 for 循环基本用法

# 假设有一个学生名单
students = ["小明", "小红", "小刚"]

# 用for循环依次打印每个学生的名字
for student in students:
    print(student)  # 每次循环,student变量会依次等于列表中的每个名字

# 小明
# 小红
# 小刚

5.2 for 循环与 range 的结合

# 计算1到10的累加和
total = 0  # 用于累加的变量
# 使用for循环遍历1到10的整数序列
# 使用range()函数生成1到10的整数序列
# range()函数可以生成一个整数序列,包含从开始到结束(不包含结束)的整数
# 例如,range(1, 11)会生成1到10的整数序列
# range(5) 会生成0到4的整数序列 [0, 1, 2, 3, 4]
for number in range(1, 11):
    total += number  # 每次循环把当前数字加到total上
print("1到10的和是:", total)  # 输出:1到10的和是:55
# 使用for循环遍历0到4的整数序列
# 使用range()函数生成0到4的整数序列
# range()函数可以生成一个整数序列,包含从开始到结束(不包含结束)的整数
# 例如,range(5)会生成0到4的整数序列
for i in range(5):  # range(5)生成0,1,2,3,4
    print(i)
# for循环可用于循环字符串
for char in "Hello":
    print(char)

5.3 while 循环:只要条件成立就一直循环

# 用while循环打印1到5
n = 1
while n <= 5:  # 只要n小于等于5就继续循环
    print(n)
    n += 1  # 每次循环n加1

5.4 break:遇到特殊情况提前结束循环

# 打印1到100,但遇到第一个能被7整除的数就停止
for i in range(1, 101):
    if i % 7 == 0:  # 如果i能被7整除
        print("遇到第一个能被7整除的数:", i)
        break  # 立即结束整个循环
    print(i)

5.5 continue:跳过本次循环,直接进入下一轮

# 打印1到10中的所有奇数
for i in range(1, 11):
    if i % 2 == 0:  # 如果是偶数
        continue    # 跳过本次循环,后面的print不会执行
    print(i)        # 只会打印奇数

5.6 综合案例

# 给每个同学打印Hello, xxx!
names = ["Tom", "Jerry", "Spike"]
for name in names:
    print(f"Hello, {name}!")  # f-string格式化输出

5.7 死循环示例

# 下面的代码会无限循环,除非强制停止
while True:
    print("这是一个死循环,按Ctrl+C可以终止程序。")

5.8 总结

  • for循环适合遍历列表、字符串、range等序列。
  • while循环适合只要条件成立就一直执行的场景。
  • break可以提前终止循环,continue可以跳过本轮循环。
  • 死循环会让程序永远运行下去,通常要避免。

6. 切片

切片是Python中一种强大的数据操作方式,它允许我们从序列(如列表、元组、字符串)中提取部分元素。

6.1 基础切片操作

# 创建一个包含5个名字的列表
names = ['小明', '小红', '小华', '小李', '小张']

# 获取前三个名字
first_three = names[0:3]  # 从索引0开始,到索引3结束(不包含3)
print(first_three)  # 输出: ['小明', '小红', '小华']

# 当从开头开始切片时,可以省略起始索引
first_three_short = names[:3]  # 效果同上
print(first_three_short)  # 输出: ['小明', '小红', '小华']

6.2 切片的基本语法

切片的基本语法是:序列[起始索引:结束索引:步长]

# 创建一个数字列表
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 获取中间部分
middle = numbers[2:5]  # 获取索引2到4的元素
print(middle)  # 输出: [2, 3, 4]

# 使用步长
# 步长为2,每隔一个取一个元素,注意是包括第一个元素,从0开始
every_other = numbers[::2]  # 每隔一个取一个元素
print(every_other)  # 输出: [0, 2, 4, 6, 8]

6.3 负索引切片

Python支持使用负索引进行切片,-1表示最后一个元素:

# 使用负索引
fruits = ['苹果', '香蕉', '橙子', '葡萄', '西瓜']

# 获取最后两个水果
last_two = fruits[-2:]  # 从倒数第二个到末尾
print(last_two)  # 输出: ['葡萄', '西瓜']

# 获取倒数第二个到倒数第三个
middle_fruits = fruits[-3:-1]  # 从倒数第三个到倒数第二个
print(middle_fruits)  # 输出: ['橙子', '葡萄']

6.4 字符串切片

# 字符串切片示例
text = "Python编程很有趣"

# 获取前6个字符
first_five = text[:6]  # 输出: "Python"
print(first_five)

# 获取最后3个字符
last_three = text[-3:]  # 输出: "很有趣"
print(last_three)

# 每隔一个字符取一个
every_other_char = text[::2]  # 输出: "Pto编很趣"
print(every_other_char)

6.5 应用示例

# 创建一个学生成绩列表
scores = [85, 92, 78, 90, 88, 95, 82, 87, 91, 89]

# 获取前5名学生的成绩
# 使用sorted函数对scores列表进行排序,并使用reverse=True参数进行降序排序
# 然后使用切片[:5]获取前5名学生的成绩
top_five = sorted(scores, reverse=True)[:5]
print("前5名成绩:", top_five)  # 输出: [95, 92, 91, 90, 89]

# 获取后3名学生的成绩
# 使用sorted函数对scores列表进行排序,并使用reverse=False参数进行升序排序
# 然后使用切片[:3]获取后3名学生的成绩
bottom_three = sorted(scores)[:3]
print("后3名成绩:", bottom_three)  # 输出: [78, 82, 85]

6.6 总结

  • 切片操作可以轻松获取序列中的部分元素
  • 切片语法:序列[起始:结束:步长]
  • 可以使用正索引和负索引
  • 适用于列表、元组、字符串等序列类型
  • 通过合理使用切片,可以大大简化代码

7. 迭代

迭代是Python中一种优雅的遍历数据的方式,它让我们能够轻松地访问序列中的每个元素。

7.1 基础迭代操作

# 创建一个水果列表
fruits = ['苹果', '香蕉', '橙子', '葡萄']

# 使用for循环遍历列表
for fruit in fruits:  # 每次循环,fruit变量会依次获取列表中的一个元素
    print(f"我喜欢吃{fruit}")  # 打印每个水果

7.2 字典的迭代

# 创建一个学生成绩字典
scores = {
    '小明': 95,
    '小红': 88,
    '小华': 92
}

# 迭代字典的键
print("学生名单:")
for name in scores:  # 默认迭代字典的键
    print(name)

# 迭代字典的值
print("\n成绩列表:")
for score in scores.values():  # 使用values()方法获取所有值
    print(score)

# 同时迭代键和值
print("\n学生成绩:")
for name, score in scores.items():  # 使用items()方法同时获取键和值
    print(f"{name}的成绩是{score}分")

7.3 整数不是可迭代对象

# 创建一个整数
number = 10

# 迭代整数
print("整数中的每个数字:")
for digit in number: # TypeError: 'int' object is not iterable
    print(digit)

7.4 字符串的迭代

# 创建一个字符串
message = "你好,Python!"

# 迭代字符串中的每个字符
print("字符串中的每个字符:")
for char in message:  # 每次循环,char变量会获取一个字符
    print(char)

7.5 使用enumerate进行索引迭代

# 同时获取元素的索引和值:
# 创建一个购物清单
shopping_list = ['牛奶', '面包', '鸡蛋', '水果']

# 使用enumerate同时获取索引和值
print("购物清单:")
for index, item in enumerate(shopping_list):  # enumerate会返回(索引, 值)对
    print(f"{index + 1}. {item}")  # 索引从0开始,所以加1显示

7.6 应用示例

# 创建一个学生信息列表,列表的每个元素是一个元组,元组中包含学生的姓名、年龄和性别
students = [
    ('小明', 15, '男'),
    ('小红', 14, '女'),
    ('小华', 15, '男'),
    ('小丽', 14, '女')
]

# 使用迭代处理学生信息
print("学生信息统计:")
boys = 0  # 男生计数
girls = 0  # 女生计数
total_age = 0  # 年龄总和

for name, age, gender in students:  # 解包元组中的值
    total_age += age  # 累加年龄
    if gender == '男':
        boys += 1
    else:
        girls += 1

# 计算平均年龄
average_age = total_age / len(students)

print(f"班级共有{len(students)}名学生")
print(f"男生:{boys}人,女生:{girls}人")
print(f"平均年龄:{average_age:.1f}岁")

7.7 总结

  • 迭代是Python中遍历数据的基本方式
  • 可以使用for循环遍历任何可迭代对象
  • 字典可以通过keys()、values()、items()方法进行不同方式的迭代
  • enumerate()函数可以同时获取索引和值
  • 迭代操作让代码更简洁、更易读

8. 列表生成式

列表生成式是Python中一种优雅的创建列表的方式,它让我们能够用一行代码完成复杂的列表创建操作。

8.1 基础列表生成式

# 创建一个包含1到10的平方的列表
# 列表生成式的基本语法:[表达式 for 变量 in 可迭代对象]
# 这里的表达式是 x * x,变量是 x,可迭代对象是 range(1, 11)
# 它会遍历 range(1, 11) 中的每个数字,计算其平方,然后放入新列表中
squares = [x * x for x in range(1, 11)]  # 遍历1到10,计算每个数的平方
print(squares)  # 输出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 对比传统方法
squares_traditional = []  # 创建空列表
for x in range(1, 11):  # 使用for循环
    squares_traditional.append(x * x)  # 添加平方数
print(squares_traditional)  # 输出相同结果

8.2 带条件的列表生成式

# 在列表生成式中添加条件来筛选元素:

# 创建一个只包含偶数的平方的列表
# 列表生成式的基本语法:[表达式 for 变量 in 可迭代对象 if 条件]
# 这里的表达式是 x * x,变量是 x,可迭代对象是 range(1, 11),条件是 x % 2 == 0
# 它会遍历 range(1, 11) 中的每个数字,如果它是偶数,则计算其平方,然后放入新列表中
even_squares = [x * x for x in range(1, 11) if x % 2 == 0]  # 只选择偶数
print(even_squares)  # 输出: [4, 16, 36, 64, 100]

# 创建一个包含正数的列表
numbers = [-2, -1, 0, 1, 2, 3]
# 列表生成式的基本语法:[表达式 for 变量 in 可迭代对象 if 条件]
# 这里的表达式是 x,变量是 x,可迭代对象是 numbers,条件是 x > 0
# 它会遍历 numbers 中的每个数字,如果它是正数,则放入新列表中
positive_numbers = [x for x in numbers if x > 0]  # 只选择正数
print(positive_numbers)  # 输出: [1, 2, 3]

8.3 多重循环的列表生成式

# 列表生成式支持多重循环,可以生成复杂的组合:

# 创建一个包含所有可能的字母组合的列表
letters = ['A', 'B', 'C']
numbers = ['1', '2', '3']
# 列表生成式的基本语法:[表达式 for 变量 in 可迭代对象 for 变量 in 可迭代对象]
# 这里的表达式是 letter + number,变量是 letter 和 number,可迭代对象是 letters 和 numbers
# 它会遍历 letters 中的每个字母,然后遍历 numbers 中的每个数字,将它们组合起来,然后放入新列表中
combinations = [letter + number for letter in letters for number in numbers]  # 双重循环
print(combinations)  # 输出: ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']

8.4 应用示例

# 创建一个学生成绩列表
scores = [
    {'name': '小明', 'score': 85},
    {'name': '小红', 'score': 92},
    {'name': '小华', 'score': 78},
    {'name': '小李', 'score': 90}
]

# 获取所有及格学生的名字
passing_students = [student['name'] for student in scores if student['score'] >= 60]
print("及格学生:", passing_students)  # 输出: ['小明', '小红', '小华', '小李']

# 计算每个学生的成绩等级
grade_list = [
    f"{student['name']}: {'优秀' if student['score'] >= 90 else '良好' if student['score'] >= 80 else '及格' if student['score'] >= 60 else '不及格'}"
    for student in scores
]
print("成绩等级:", grade_list)

8.5 列表生成式与传统for循环的效率比较

列表生成式通常被认为是一种更简洁、更优雅的创建列表的方式

8.5.1 列表生成式的效率

  • 执行速度:列表生成式在Python内部是经过优化的,通常比等效的for循环执行得更快。
  • 内存使用:列表生成式在内部一次性分配内存,而不是像for循环那样逐步扩展列表。
  • 代码简洁:用一行代码替代多行for循环,减少了出错的可能性。

8.5.2 传统for循环的效率

  • 执行速度:传统for循环在Python中执行速度相对较慢,尤其是在处理大型数据集时。
  • 内存使用:传统for循环需要多次扩展列表,导致内存使用效率较低。
  • 代码复杂:传统for循环的代码行数较多,容易出错。

8.5.3 结论

  • 列表生成式在执行速度和内存使用上通常比传统for循环更高效。
  • 列表生成式在代码简洁性上具有明显优势。
  • 在处理大型数据集时,列表生成式通常比传统for循环更高效。

8.6 总结

  • 列表生成式可以大大简化列表的创建过程
  • 可以使用条件语句筛选元素
  • 支持多重循环生成复杂组合
  • 代码更简洁、更易读
  • 适合处理数据转换和筛选

9. 正则表达式

9.1 正则表达式的基本语法

正则表达式本质上就是一串特殊的字符,每个字符或符号都有特定含义。
下面是一些常用的正则表达式符号及其作用:

  • \d:匹配一个数字(0-9)
  • \w:匹配一个字母、数字或下划线
  • .:匹配任意单个字符(除了换行符)
  • *:匹配前面的内容出现0次或多次
  • +:匹配前面的内容出现1次或多次
  • ?:匹配前面的内容出现0次或1次
  • {n}:匹配前面的内容出现n次
  • {n,m}:匹配前面的内容出现n到m次
  • []:匹配括号内的任意一个字符
  • ^:匹配字符串的开头
  • $:匹配字符串的结尾
  • |:表示“或”,比如A|B可以匹配A或B

9.2 基础匹配

re.match(pattern, string) :从字符串开头匹配正则表达式。

import re  # 导入正则表达式模块

# 匹配3位数字
print(re.match(r'\d\d\d', '123'))  # 匹配成功,输出Match对象
# 输出:<re.Match object; span=(0, 3), match='123'>

# 匹配字母+字母+数字
print(re.match(r'\w\w\d', 'ab3'))  # 匹配成功

# 匹配以py开头,后面跟任意一个字符
print(re.match(r'py.', 'pyx'))  # 匹配成功

# 注:r前缀表示“原始字符串”,这样写正则表达式时不用担心转义问题。


**1. `re.Match object`**
-   这是 Python `re` 模块返回的匹配对象,表示正则表达式匹配成功。
-   如果匹配失败,`re.match()` 会返回 `None`。

**2. `span=(0, 3)`**
-   表示匹配到的子串在目标字符串中的 **起始和结束位置(左闭右开区间)** 。
    -   `0` 是匹配的起始索引(包含)
    -   `3` 是匹配的结束索引(不包含)
-   说明匹配的字符串是目标字符串的第 `0` 到 `2` 个字符(Python 字符串索引从 `0` 开始)。

**3. `match='123'`**
-   表示实际匹配到的字符串内容。
-   在这个例子中,正则表达式 `\d\d\d` 匹配了 `'123'`。

9.3 判断字符串是否符合某种格式

# 判断一个字符串是不是合法的电话号码(区号-号码):

import re

# 匹配3位数字-3到8位数字
pattern = r'^\d{3}-\d{3,8}$'
print(re.match(pattern, '010-12345'))  # 匹配成功
print(re.match(pattern, '010 12345'))  # 匹配失败(有空格)

# 注:  `^`表示开头,`$`表示结尾,保证整个字符串都要符合规则。

9.4 切分字符串

# 有时候我们需要把字符串按空格、逗号等分割成列表。用正则表达式可以一次性处理多种分隔符:

import re

text = 'a,b;; c  d'
# 匹配空格、逗号、分号中任意一个或多个
result = re.split(r'[\s,;]+', text)
print(result)  # 输出:['a', 'b', 'c', 'd']

# 注:  `[\s,;]+`表示空格、逗号、分号中任意一个,出现一次或多次。

9.5 提取子串(分组)

正则表达式可以用括号()把需要提取的部分分组,方便后续获取:

m.groups() :返回所有捕获组(括号 () 内的匹配内容)。
m.group(n) :返回第n个捕获组(括号 () 内的匹配内容)。

import re

# 匹配区号和本地号码,并分组提取
m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
if m:
    print(m.group(1))  # 输出:010(区号)
    print(m.group(2))  # 输出:12345(本地号码)
    
注: `group(0)`是整个匹配内容,`group(1)`、`group(2)`分别是第12个括号里的内容。

9.6 匹配变量名、邮箱等复杂规则

  • [a-zA-Z_][0-9a-zA-Z_]*:匹配Python合法变量名(字母或下划线开头,后面跟字母、数字或下划线)
  • \w+@\w+.\w+:简单匹配邮箱(实际邮箱规则更复杂)

9.7 贪婪与非贪婪匹配

正则默认是“贪婪”的,会尽可能多地匹配字符。
如果想让它“少匹配”,可以在量词后加?,变成“非贪婪”:

import re

# 贪婪匹配
m1 = re.match(r'^(\d+)(0*)$', '102300')
print(m1.groups())  # 输出:('102300', '')

# 非贪婪匹配
m2 = re.match(r'^(\d+?)(0*)$', '102300')
print(m2.groups())  # 输出:('1023', '00')



**(1) 贪婪匹配 `r'^(\d+)(0*)$'`**
   `^`:匹配字符串开头。
   `(\d+)`:**贪婪匹配** 1 个或多个数字(`+` 是贪婪的,会尽可能匹配更多字符)。
   `(0*)`:匹配 0 个或多个 `0`(`*` 也是贪婪的,但前面的 `\d+` 已经匹配了所有数字)。
   `$`:匹配字符串结尾。

    **匹配过程**:
      `\d+` 贪婪匹配,直接吃掉整个 `'102300'`。
      `0*` 只能匹配剩下的空字符串 `''`(因为 `\d+` 已经匹配了所有数字)。
      最终分组:
           `(\d+)` → `'102300'`
           `(0*)` → `''`
      输出:('102300', '')
  
 **(2) 非贪婪匹配 `r'^(\d+?)(0*)$'`**
    `(\d+?)`:**非贪婪匹配** 1 个或多个数字(`+?` 是非贪婪的,尽可能匹配最少字符)。
    `(0*)`:仍然贪婪匹配 `0`。
    **匹配过程**:
         `\d+?` 非贪婪匹配,只匹配 `'1'`(最小匹配),但为了满足后面的 `(0*)$`,它必须匹配到 `'1023'`(否则 `(0*)` 无法匹配剩余部分)。

     `0*` 匹配剩下的 `'00'`。

      最终分组:
        `(\d+?)` → `'1023'`(尽可能少匹配,但仍要保证整个正则能匹配)
        `(0*)` → `'00'`
     输出:('1023', '00')

9.8 正则表达式在Python中的使用方法

Python的re模块提供了多种常用方法:

  • re.match(pattern, string):从头开始匹配
  • re.search(pattern, string):搜索整个字符串
  • re.split(pattern, string):按正则分割字符串
  • re.findall(pattern, string):找出所有匹配内容
  • re.sub(pattern, repl, string):替换匹配内容

9.9 常用方法

import re

# search
m = re.search(r'\d+', 'a1b22c333')
print(m.group())  # 输出:1

# findall
numbers = re.findall(r'\d+', 'a1b22c333')
print(numbers)  # 输出:['1', '22', '333']

# 替换所有数字为X
result = re.sub(r'\d+', 'X', 'a1b22c333')
print(result)  # 输出:aXbXcX

9.10 正则表达式的预编译

如果你需要多次使用同一个正则表达式,可以先“编译”它,提高效率:

import re

# 预编译正则表达式
email_pattern = re.compile(r'^\w+@\w+\.\w+$')

# 多次使用
print(email_pattern.match('test@abc.com'))  # 匹配成功
print(email_pattern.match('bad-email'))     # 匹配失败

9.11 验证邮箱地址

# 判断邮箱地址是否合法:

import re

def is_valid_email(addr):
    # 匹配邮箱的正则表达式:用户名@域名
    pattern = r'^[\w\.]+@\w+\.\w+$'
    # 如果匹配成功,返回True,否则返回False
    return re.match(pattern, addr) is not None

# 测试
print(is_valid_email('someone@gmail.com'))  # True
print(is_valid_email('bill.gates@microsoft.com'))  # True
print(is_valid_email('bob#example.com'))  # False
print(is_valid_email('mr-bob@example.com'))  # False

# 注: 这里用户名允许字母、数字、下划线和点,域名部分只允许字母和点。

9.12 提取带名字的邮箱

# 有些邮箱地址前面带有名字,比如`<Tom Paris> tom@voyager.org`。我们希望提取出名字部分:

import re

def name_of_email(addr):
    # 匹配带名字的邮箱
    m = re.match(r'^<([\w\s]+)>\s*([\w\.]+)@(\w+\.\w+)$', addr)
    if m:
        return m.group(1)  # 返回名字部分
    # 匹配不带名字的邮箱
    m = re.match(r'^([\w\.]+)@(\w+\.\w+)$', addr)
    if m:
        return m.group(1)  # 返回邮箱用户名
    return None

# 测试
print(name_of_email('<Tom Paris> tom@voyager.org'))  # Tom Paris
print(name_of_email('tom@voyager.org'))  # tom

#   先尝试匹配带名字的格式,如果不匹配再尝试普通邮箱格式。