avatar

青青子衿的拾枝杂谈

A text-focused Halo theme

  • 首页
  • linux基础
  • Linux系统
  • Linux高级
  • nginx
  • k8s
  • 网络
Home Python编程
文章

Python编程

Posted recently Updated recently
By 青青子衿
132~170 min read

Python 编程:从入门到运维实战


目录

  1. Python 简介与环境部署
  2. 基本使用:交互式与脚本模式
  3. 变量与数据类型
  4. 运算符
  5. 流程控制:if/elif/else
  6. for 循环
  7. while 循环
  8. 循环控制进阶
  9. 数据类型深入
  10. 函数
  11. switch 语句替代方案
  12. 模块与代码封装
  13. 反射机制
  14. 正则表达式
  15. 文件 IO 与操作系统交互
  16. 异常处理
  17. 装饰器
  18. 面向对象编程
  19. MySQL 数据库操作

1. Python 简介与环境部署

1.1 Python 是什么?

类比:如果说 C 语言像手动挡汽车——功能强大但操作复杂,那 Python 就像自动挡汽车——同样能到达目的地,但驾驶体验轻松得多。

Python 是一种解释型、面向对象的高级编程语言,诞生于 1989 年,由荷兰程序员 Guido van Rossum 在圣诞节期间开发。名字来源于他喜爱的喜剧团体 "Monty Python"。

Python 在运维中的地位:

  • Google 用 Python 编写网页爬虫和搜索引擎组件
  • NASA 在多个系统中使用 Python 作为脚本语言
  • YouTube 的大部分服务由 Python 编写
  • 几乎所有 Linux 发行版都预装了 Python

1.2 Python 2 vs Python 3

特性Python 2Python 3
默认编码ASCIIUTF-8
print 语句print "hello"print("hello")
整数除法3/2 = 13/2 = 1.5
官方支持2020年已停止维护持续更新中
字符串str 是字节串str 是 Unicode

1.3 环境安装

Linux 源码安装(以 CentOS 为例):

# 安装依赖
[root@localhost ~]# yum install openssl openssl-devel -y

# 下载并解压源码
[root@localhost ~]# tar xf Python-3.9.7.tgz
[root@localhost ~]# cd Python-3.9.7

# 编译安装
[root@localhost Python-3.9.7]# ./configure --prefix=/usr/local/python3
[root@localhost Python-3.9.7]# make && make install

# 创建软链接
[root@localhost Python-3.9.7]# ln -s /usr/local/python3/bin/python3 /usr/bin/python3

# 验证
[root@localhost ~]# python3 -V
Python 3.9.7

Windows 安装(推荐 Anaconda/Miniconda):

  1. 访问 https://www.anaconda.com 下载 Miniconda
  2. 安装后打开 Anaconda Prompt
  3. 验证:python --version

1.4 虚拟环境

想一想:如果项目 A 需要 requests 2.25,项目 B 需要 requests 2.28,怎么办?

就像不同的实验课需要不同的试剂,不同的项目也需要不同的 Python 环境。虚拟环境就是为每个项目创建独立的"实验室"。

# 创建虚拟环境
python3 -m venv myproject_env

# 激活虚拟环境(Linux)
source myproject_env/bin/activate

# 激活虚拟环境(Windows)
myproject_env\Scripts\activate

# 安装项目专属包
pip install requests==2.28.0

# 退出虚拟环境
deactivate

2. 基本使用:交互式与脚本模式

2.1 交互式模式——即问即答

类比:交互式模式就像在计算器上按一个算一个,马上就能看到结果。

>>> print("Hello, World!")
Hello, World!
>>> 2 + 3
5
>>> 100 / 3
33.333333333333336
>>> exit()

提示:安装 ipython(pip install ipython)可以获得更好的交互体验,支持代码补全和语法高亮。

2.2 脚本模式——编写程序

类比:脚本模式就像把解题步骤写在纸上,一次性执行。

创建文件 hello.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

print("Hello, World!")
print("欢迎来到 Python 的世界!")

运行方式:

# Linux
[root@localhost ~]# python3 hello.py
Hello, World!
欢迎来到 Python 的世界!

# Windows
C:\> python hello.py

2.3 文件类型

扩展名说明
.pyPython 源代码文件
.pyc编译后的字节码文件(加速加载)
.pyo优化后的字节码文件

思考题:为什么运行一次 .py 文件后会出现 __pycache__ 文件夹?

答案:Python 解释器会自动将 .py 文件编译为 .pyc 存入该目录,下次运行时若源码未变则跳过编译,直接加载 .pyc,从而加快启动速度。

2.4 编码声明

在 Python 3 中,源码默认使用 UTF-8 编码,一般不需要额外声明。但如果文件包含中文,建议在文件开头加上:

# -*- coding: utf-8 -*-

3. 变量与数据类型

3.1 变量——带标签的盒子

类比:变量就像一个贴了标签的盒子,你可以往盒子里放东西(赋值),也可以换掉里面的东西。盒子上的标签就是变量名。

>>> name = "张三"       # 字符串
>>> age = 17            # 整数
>>> height = 1.75       # 浮点数
>>> is_student = True   # 布尔值
>>> print(f"{name},{age}岁,身高{height}米")
张三,17岁,身高1.75米

变量命名规则:

  • 由字母、数字、下划线组成
  • 不能以数字开头
  • 不能使用 Python 关键字(如 if、for、class 等)
  • 建议使用有意义的英文命名,如 user_name 而非 a
>>> # 正确的命名
>>> user_name = "admin"
>>> server_count = 5
>>> _private_var = "secret"

>>> # 错误的命名
>>> 1user = "admin"      # SyntaxError: 数字开头
>>> class = "math"       # SyntaxError: 关键字

3.2 Python 的四种基本数据类型

类型说明示例
int整数42, -100, 0
float浮点数(小数)3.14, -0.5, 2.0
str字符串"hello", 'world'
bool布尔值True, False
>>> a = 100
>>> type(a)
<class 'int'>

>>> b = 3.14
>>> type(b)
<class 'float'>

>>> c = "hello"
>>> type(c)
<class 'str'>

>>> d = True
>>> type(d)
<class 'bool'>

3.3 类型转换

运维场景:从配置文件读取的端口号是字符串 "8080",需要转为整数才能进行比较。

>>> port_str = "8080"
>>> port_int = int(port_str)      # 字符串转整数
>>> port_int
8080

>>> price = 9.99
>>> price_str = str(price)        # 浮点数转字符串
>>> price_str
'9.99'

>>> # 常见转换函数
>>> int("123")       # 123
>>> float("3.14")    # 3.14
>>> str(100)         # '100'
>>> bool(0)          # False
>>> bool("")         # False
>>> bool("hello")    # True(非空字符串为 True)

3.4 多重赋值与变量交换

# 多重赋值
>>> x = y = z = 0

# 多元赋值
>>> a, b, c = 1, 2, 3

# 变量交换(Python 的优雅写法)
>>> x, y = 10, 20
>>> x, y = y, x       # 一行完成交换!
>>> print(x, y)
20 10

想一想:在 C 语言中交换两个变量需要一个临时变量 temp,Python 为什么不需要?

因为 Python 在等号右边先创建了一个元组 (y, x),然后再解包赋值给左边的 x, y。

3.5 变量的引用机制

重要概念:Python 中,变量不是"盒子",而是"标签"。同一个值可以贴多个标签。

>>> a = 123
>>> b = 123
>>> id(a)          # 查看内存地址
140712345678912
>>> id(b)          # 地址相同!
140712345678912

>>> a = 456        # a 换了个标签,123 还在内存中
>>> id(a)          # 地址变了
140712345679008

4. 运算符

4.1 算术运算符

类比:算术运算符就是数学课上的加减乘除,只不过多了几个"新朋友"。

>>> 10 + 3       # 加法
13
>>> 10 - 3       # 减法
7
>>> 10 * 3       # 乘法
30
>>> 10 / 3       # 除法(Python3 返回浮点数)
3.3333333333333335
>>> 10 // 3      # 整除(只取整数部分)
3
>>> 10 % 3       # 取余(求模)
1
>>> 2 ** 10      # 幂运算(2的10次方)
1024

运维实用示例:

# 计算磁盘使用率
total_disk = 500    # GB
used_disk = 347     # GB
usage_percent = (used_disk / total_disk) * 100
print(f"磁盘使用率: {usage_percent:.1f}%")
# 输出: 磁盘使用率: 69.4%

# 判断端口号是否合法(1-65535)
port = 8080
is_valid = 1 <= port <= 65535
print(f"端口 {port} 是否合法: {is_valid}")

4.2 比较运算符

>>> 5 > 3        # True
>>> 5 == 3       # False
>>> 5 != 3       # True
>>> 5 >= 5       # True
>>> "abc" == "abc"   # True(字符串比较)

4.3 逻辑运算符

类比:逻辑运算符就像做条件判断——"与"是两票都赞成才通过,"或"是一票赞成就可以,"非"是唱反调。

>>> True and False    # 与:两个都为 True 才是 True
False
>>> True or False     # 或:一个为 True 就是 True
True
>>> not True          # 非:取反
False

# 实际例子:判断服务器是否正常
cpu_ok = True
mem_ok = True
disk_ok = False

server_healthy = cpu_ok and mem_ok and disk_ok
print(f"服务器状态正常: {server_healthy}")
# 输出: 服务器状态正常: False

4.4 赋值运算符

>>> x = 10
>>> x += 5      # 等价于 x = x + 5
>>> x
15
>>> x -= 3      # 等价于 x = x - 3
>>> x
12
>>> x *= 2      # 等价于 x = x * 2
>>> x
24
>>> x //= 5     # 等价于 x = x // 5
>>> x
4

思考题:以下代码输出什么?为什么?

a = 2 + 5 * 3
print(a)

答案:17。因为乘法优先级高于加法,先算 5*3=15,再算 2+15=17。


5. 流程控制:if/elif/else

5.1 为什么需要流程控制?

类比:流程控制就像导航软件——根据路况(条件)选择不同的路线(代码分支)。

问题:如何编写一个脚本,根据磁盘使用率发出不同的警告?

5.2 基本语法

# 单分支
if 条件:
    执行代码

# 双分支
if 条件:
    执行代码A
else:
    执行代码B

# 多分支
if 条件1:
    执行代码A
elif 条件2:
    执行代码B
elif 条件3:
    执行代码C
else:
    执行代码D

5.3 实战:磁盘使用率监控

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

usage = int(input("请输入磁盘使用率(0-100): "))

if usage >= 90:
    print("【严重】磁盘使用率超过90%,请立即清理!")
elif usage >= 80:
    print("【警告】磁盘使用率超过80%,建议清理。")
elif usage >= 70:
    print("【提醒】磁盘使用率超过70%,请关注。")
else:
    print("【正常】磁盘状态良好。")

运行示例:

请输入磁盘使用率(0-100): 85
【警告】磁盘使用率超过80%,建议清理。

5.4 嵌套条件

#!/usr/bin/env python3

is_raining = int(input("下雨吗?[1=是, 0=否]: "))
if is_raining == 1:
    has_umbrella = int(input("有伞吗?[1=有, 0=没有]: "))
    if has_umbrella == 1:
        print("带伞出门")
    else:
        print("在家待着吧!")
else:
    print("直接出门")

5.5 三元运算(简洁写法)

>>> age = 20
>>> status = "成年" if age >= 18 else "未成年"
>>> print(status)
成年

想一想:以下成绩评级代码有什么问题?

score = 95
if score >= 90:
    print("A")
if score >= 80:
    print("B")
if score >= 70:
    print("C")
else:
    print("不及格")

问题:输出 A B C,因为每个 if 是独立的!应该用 elif 使它们互斥。


6. for 循环

6.1 为什么需要循环?

类比:for 循环就像购物清单——你逐项完成,直到清单上的东西全部买完。

问题:如何快速检查 100 台服务器的连通性?逐台手动 ping 显然不现实。

6.2 基本语法

for 变量 in 序列:
    循环体

6.3 range() 函数——数字序列生成器

# range(stop) —— 从0到stop-1
>>> for i in range(5):
...     print(i, end=" ")
0 1 2 3 4

# range(start, stop) —— 从start到stop-1
>>> for i in range(1, 6):
...     print(i, end=" ")
1 2 3 4 5

# range(start, stop, step) —— 指定步长
>>> for i in range(0, 10, 2):
...     print(i, end=" ")
0 2 4 6 8

6.4 遍历序列

# 遍历字符串
for char in "Hello":
    print(char, end=" ")
# 输出: H e l l o

# 遍历列表
servers = ["web01", "web02", "db01", "cache01"]
for server in servers:
    print(f"正在检查服务器: {server}")

# 使用 enumerate() 同时获取索引和值
for index, server in enumerate(servers):
    print(f"第{index+1}台服务器: {server}")

输出:

第1台服务器: web01
第2台服务器: web02
第3台服务器: db01
第4台服务器: cache01

6.5 实战:计算 1 到 100 的和

total = 0
for i in range(1, 101):
    total += i
print(f"1到100的和 = {total}")
# 输出: 1到100的和 = 5050

6.6 实战:九九乘法表

for i in range(1, 10):
    for j in range(1, i + 1):
        print(f"{j}x{i}={i*j}", end="\t")
    print()  # 换行

输出:

1x1=1
1x2=2   2x2=4
1x3=3   2x3=6   3x3=9
1x4=4   2x4=8   3x4=12  4x4=16
...

7. while 循环

7.1 while vs for

类比:for 循环像按计划执行的旅行(已知天数),while 循环像条件触发的警报——只要条件满足就一直响。

7.2 基本语法

while 条件:
    循环体

7.3 基本示例

# 倒计时
count = 5
while count > 0:
    print(f"倒计时: {count}")
    count -= 1
print("发射!")

7.4 break 和 continue

# break:跳出整个循环
while True:
    user_input = input("输入命令 (q退出): ")
    if user_input == 'q':
        print("退出程序")
        break
    print(f"执行命令: {user_input}")

# continue:跳过本次循环,继续下一次
for i in range(10):
    if i == 5:
        continue    # 跳过5
    print(i, end=" ")
# 输出: 0 1 2 3 4 6 7 8 9

7.5 while-else 语法

想一想:怎么知道循环是正常结束的还是被 break 中断的?

# while-else: 循环正常结束时执行 else
count = 0
while count < 5:
    print(count, end=" ")
    count += 1
else:
    print("\n循环正常结束!")
# 输出: 0 1 2 3 4
#       循环正常结束!

# 如果被 break 中断,else 不执行
count = 0
while count < 5:
    if count == 3:
        print("\n被中断了!")
        break
    print(count, end=" ")
    count += 1
else:
    print("这行不会执行")
# 输出: 0 1 2
#       被中断了!

7.6 实战:模拟登录(3 次机会)

#!/usr/bin/env python3
import getpass

correct_user = "admin"
correct_pass = "123456"
max_attempts = 3

for attempt in range(1, max_attempts + 1):
    username = input(f"第{attempt}次登录 - 用户名: ")
    password = input("密码: ")

    if username == correct_user and password == correct_pass:
        print("登录成功!欢迎回来,管理员。")
        break
    else:
        remaining = max_attempts - attempt
        if remaining > 0:
            print(f"用户名或密码错误,还剩{remaining}次机会。\n")
        else:
            print("登录失败次数过多,账号已锁定,请明天再试。")

8. 循环控制进阶

8.1 for-else 语法

# 查找素数的经典写法
for n in range(2, 20):
    for x in range(2, n):
        if n % x == 0:
            break
    else:
        # 循环正常结束(没有 break),说明是素数
        print(f"{n} 是素数")

输出:

2 是素数
3 是素数
5 是素数
7 是素数
11 是素数
13 是素数
17 是素数
19 是素数

8.2 嵌套循环

# 打印矩形
for i in range(4):          # 4行
    for j in range(8):      # 每行8个星号
        print("*", end="")
    print()                 # 换行

输出:

********
********
********
********

8.3 循环优化技巧

# 不好的写法:在循环内重复计算
for i in range(1000):
    result = len(some_list) * 2 + i    # len() 每次都调用

# 好的写法:提前计算
list_len = len(some_list)
double_len = list_len * 2
for i in range(1000):
    result = double_len + i

思考题:编写脚本,生成 1000 个文件名(file_0001.txt ~ file_1000.txt),如何实现?

for i in range(1, 1001):
    filename = f"file_{i:04d}.txt"
    # 创建文件...
    print(filename)
# 输出: file_0001.txt, file_0002.txt, ..., file_1000.txt

9. 数据类型深入

9.1 列表 (list)——购物清单

类比:列表就像一张购物清单,可以添加、删除、修改项目,顺序很重要。

# 创建列表
servers = ["web01", "web02", "db01", "cache01"]
ports = [80, 443, 3306, 6379]

# 访问元素(索引从0开始)
>>> servers[0]
'web01'
>>> servers[-1]       # 最后一个元素
'cache01'

# 切片
>>> servers[1:3]      # 取索引1到2
['web02', 'db01']
>>> servers[:2]       # 前两个
['web01', 'web02']

# 常用方法
servers.append("monitor01")     # 末尾添加
servers.insert(0, "gateway")    # 指定位置插入
servers.remove("web02")         # 删除指定元素
popped = servers.pop()          # 弹出最后一个元素
servers.sort()                  # 排序
print(len(servers))             # 长度

# 列表推导式(Pythonic 写法)
squares = [x**2 for x in range(1, 6)]
print(squares)  # [1, 4, 9, 16, 25]

# 过滤:找出大于80的端口
big_ports = [p for p in ports if p > 1000]
print(big_ports)  # [3306, 6379]

9.2 元组 (tuple)——不可修改的列表

类比:元组就像一个密封的档案袋——内容一旦放入就不能更改,适合存放不应该被修改的数据。

# 创建元组
server_info = ("web01", "192.168.1.10", 80)
coordinates = (39.9, 116.4)

# 访问
>>> server_info[0]
'web01'

# 不能修改!
>>> server_info[0] = "web02"
TypeError: 'tuple' object does not support item assignment

# 元组解包
hostname, ip, port = server_info
print(f"主机: {hostname}, IP: {ip}, 端口: {port}")

# 单元素元组要加逗号
single = (42,)    # 这是元组
not_tuple = (42)  # 这只是数字 42

9.3 字典 (dict)——带标签的收纳柜

类比:字典就像一个带标签的收纳柜,每个格子上贴着标签(键),里面放着物品(值),通过标签快速找到对应的物品。

# 创建字典
server = {
    "hostname": "web01",
    "ip": "192.168.1.10",
    "os": "CentOS 7.9",
    "cpu_cores": 4,
    "memory_gb": 8
}

# 访问
>>> server["hostname"]
'web01'
>>> server.get("disk", "未配置")   # 安全获取,不存在返回默认值
'未配置'

# 增删改
server["disk_gb"] = 500           # 添加
server["memory_gb"] = 16           # 修改
del server["os"]                   # 删除

# 遍历
for key, value in server.items():
    print(f"{key}: {value}")

# 实用:统计字符出现次数
text = "hello world"
char_count = {}
for char in text:
    char_count[char] = char_count.get(char, 0) + 1
print(char_count)
# {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}

9.4 集合 (set)——去重利器

类比:集合就像一个签到表——每个人只能签到一次,自动去重。

# 创建集合
ips = {"192.168.1.1", "192.168.1.2", "192.168.1.1"}  # 自动去重
print(ips)  # {'192.168.1.1', '192.168.1.2'}

# 集合运算(数学老师会喜欢这个!)
group_a = {"张三", "李四", "王五"}
group_b = {"王五", "赵六", "钱七"}

print(group_a & group_b)   # 交集: {'王五'}
print(group_a | group_b)   # 并集: {'张三', '李四', '王五', '赵六', '钱七'}
print(group_a - group_b)   # 差集: {'张三', '李四'}

# 实用:日志去重
log_ips = ["10.0.0.1", "10.0.0.2", "10.0.0.1", "10.0.0.3", "10.0.0.2"]
unique_ips = set(log_ips)
print(f"共 {len(unique_ips)} 个独立IP")
# 输出: 共 3 个独立IP

9.5 字符串方法精选

text = "  Hello, World!  "

# 常用方法
text.strip()          # 去除两端空白 -> "Hello, World!"
text.lower()          # 转小写 -> "  hello, world!  "
text.upper()          # 转大写 -> "  HELLO, WORLD!  "
text.replace("World", "Python")  # 替换
text.split(",")       # 按逗号分割 -> ['  Hello', ' World!  ']
text.find("World")    # 查找位置 -> 9(找不到返回-1)
text.count("l")       # 计数 -> 3
text.startswith("  H") # True
text.endswith("!  ")   # True

# 格式化(f-string,推荐)
hostname = "web01"
cpu = 75.5
print(f"服务器 {hostname} CPU使用率: {cpu:.1f}%")

# join:用指定字符连接列表
path_parts = ["/usr", "local", "bin"]
full_path = "/".join(path_parts)
print(full_path)  # /usr/local/bin

10. 函数

10.1 为什么需要函数?

类比:函数就像菜谱卡片——把做菜步骤写在卡片上,每次需要做这道菜时,拿出卡片照着做就行,不用重新想。

问题:如果多个地方需要检查服务器端口是否合法,每次都复制粘贴代码?

10.2 定义与调用

def check_port(port):
    """检查端口号是否合法(1-65535)"""
    if 1 <= port <= 65535:
        return True
    return False

# 调用
print(check_port(80))      # True
print(check_port(70000))   # False
print(check_port(443))     # True

10.3 参数详解

# 1. 位置参数
def greet(name, greeting):
    print(f"{greeting}, {name}!")

greet("张老师", "您好")   # 您好, 张老师!

# 2. 默认参数
def connect_server(host, port=22, timeout=30):
    print(f"连接 {host}:{port},超时{timeout}秒")

connect_server("192.168.1.10")              # 使用默认端口22
connect_server("192.168.1.10", port=8080)   # 指定端口

# 3. *args:接收任意数量的位置参数(打包为元组)
def sum_all(*args):
    print(f"收到参数: {args}")
    return sum(args)

print(sum_all(1, 2, 3))       # 6
print(sum_all(10, 20, 30, 40)) # 100

# 4. **kwargs:接收任意数量的关键字参数(打包为字典)
def config_server(**kwargs):
    for key, value in kwargs.items():
        print(f"  {key} = {value}")

print("服务器配置:")
config_server(host="192.168.1.10", port=80, workers=4)

输出:

服务器配置:
  host = 192.168.1.10
  port = 80
  workers = 4

10.4 返回值

# 返回多个值(实际返回的是元组)
def get_server_status():
    cpu = 45.2
    memory = 62.8
    disk = 78.5
    return cpu, memory, disk

cpu, mem, disk = get_server_status()
print(f"CPU: {cpu}%, 内存: {mem}%, 磁盘: {disk}%")

# 没有 return 的函数返回 None
def say_hello():
    print("Hello!")

result = say_hello()
print(result)  # None

10.5 lambda 匿名函数

类比:lambda 就像一次性便签——写个简短的公式,用完就扔。

# 普通函数
def square(x):
    return x ** 2

# lambda 等价写法
square = lambda x: x ** 2

# 常见用法:配合 sorted、map、filter
servers = [
    {"name": "web01", "cpu": 75},
    {"name": "db01", "cpu": 90},
    {"name": "cache01", "cpu": 30},
]

# 按 CPU 使用率排序
sorted_servers = sorted(servers, key=lambda s: s["cpu"])
for s in sorted_servers:
    print(f"{s['name']}: CPU {s['cpu']}%")
# 输出:
# cache01: CPU 30%
# web01: CPU 75%
# db01: CPU 90%

10.6 变量作用域

global_var = "我是全局变量"

def my_function():
    local_var = "我是局部变量"
    print(global_var)    # 可以访问全局变量
    print(local_var)     # 可以访问局部变量

my_function()
print(global_var)        # OK
# print(local_var)       # NameError: 局部变量在函数外不可见

# 使用 global 关键字在函数内修改全局变量
counter = 0

def increment():
    global counter
    counter += 1

increment()
increment()
print(counter)  # 2

11. switch 语句替代方案

11.1 问题:Python 没有 switch 语句

很多编程语言都有 switch/case 语句来实现多分支选择,但 Python 没有提供这个语法。

想一想:如果要实现一个简单计算器,用 if/elif 写起来是不是很冗长?

11.2 解决方案:字典派发表

核心思路:利用字典的键值对来模拟 switch 的功能。

# 定义运算函数
def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    if y == 0:
        return "错误:除数不能为0"
    return x / y

# 用字典模拟 switch
operations = {
    "+": add,
    "-": subtract,
    "*": multiply,
    "/": divide
}

def calculator(x, operator, y):
    # get() 获取对应函数,找不到返回 None
    func = operations.get(operator)
    if func:
        return func(x, y)
    return "不支持的运算符"

# 测试
print(calculator(10, "+", 5))    # 15
print(calculator(10, "-", 3))    # 7
print(calculator(6, "*", 7))     # 42
print(calculator(15, "/", 4))    # 3.75
print(calculator(10, "%", 3))    # 不支持的运算符

11.3 实战:命令行菜单系统

#!/usr/bin/env python3

def show_status():
    print("=== 系统状态 ===")
    print("CPU: 45%  内存: 62%  磁盘: 78%")

def show_logs():
    print("=== 最近日志 ===")
    print("[INFO] 系统正常运行中...")

def backup_data():
    print("=== 数据备份 ===")
    print("备份完成!")

def exit_program():
    print("再见!")
    exit()

# 菜单字典
menu = {
    "1": ("查看系统状态", show_status),
    "2": ("查看日志", show_logs),
    "3": ("数据备份", backup_data),
    "0": ("退出", exit_program),
}

while True:
    print("\n===== 运维菜单 =====")
    for key, (desc, _) in menu.items():
        print(f"  ({key}) {desc}")

    choice = input("请选择操作: ").strip()
    action = menu.get(choice)

    if action:
        desc, func = action
        func()
    else:
        print("无效选项,请重新输入。")

12. 模块与代码封装

12.1 什么是模块?

类比:模块就像工具箱里的不同工具——螺丝刀处理螺丝,扳手处理螺母。把功能相关的代码放在不同文件中,各司其职。

12.2 导入方式

# 方式1:导入整个模块
import os
print(os.getcwd())        # 获取当前目录

# 方式2:导入特定函数
from os.path import exists
print(exists("/etc/passwd"))   # True

# 方式3:起别名
import subprocess as sp
result = sp.run(["ls", "-l"], capture_output=True, text=True)

# 方式4:导入所有(不推荐,容易命名冲突)
from os import *

12.3 自定义模块

创建文件 server_utils.py:

# server_utils.py —— 服务器工具模块

def check_disk_usage(threshold=80):
    """检查磁盘使用率"""
    import shutil
    total, used, free = shutil.disk_usage("/")
    usage = (used / total) * 100
    if usage > threshold:
        return f"警告:磁盘使用率 {usage:.1f}% 超过阈值 {threshold}%"
    return f"正常:磁盘使用率 {usage:.1f}%"

def format_bytes(size_bytes):
    """将字节数格式化为可读大小"""
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if size_bytes < 1024:
            return f"{size_bytes:.1f} {unit}"
        size_bytes /= 1024

if __name__ == "__main__":
    # 只有直接运行此文件时才执行,被导入时不执行
    print(check_disk_usage())
    print(format_bytes(1073741824))

在另一个文件中使用:

# main.py
import server_utils

print(server_utils.check_disk_usage(90))
print(server_utils.format_bytes(5242880))

12.4 __name__ 的妙用

想一想:为什么上面的代码要用 if __name__ == "__main__"?

当一个 .py 文件被直接运行时,__name__ 的值为 "__main__";当它被作为模块导入时,__name__ 的值为模块名。这个机制让我们可以在模块中写测试代码,而不用担心导入时自动执行。

# 模块被导入时 __name__ = 模块名
# 模块被直接运行时 __name__ = "__main__"

if __name__ == "__main__":
    print("这是直接运行")
else:
    print(f"这是被 {__name__} 导入的")

12.5 常用运维模块

模块用途
os操作系统接口(路径、文件、目录)
sys系统参数(命令行参数、路径)
subprocess执行系统命令
shutil高级文件操作(复制、移动、删除)
jsonJSON 数据处理
datetime日期时间处理
re正则表达式
logging日志记录
socket网络编程

13. 反射机制

13.1 什么是反射?

类比:反射就像你不用提前知道餐厅有什么菜——你到了餐厅,看菜单(dir()),问服务员有没有某道菜(hasattr()),然后点菜(getattr())。

核心思想:通过字符串来动态访问对象(模块)的属性和方法。

13.2 四大反射函数

# 假设有一个模块 commons.py
# commons.py:
def login():
    print("登录页面")

def home():
    print("网站主页")

def logout():
    print("登出页面")

问题:用户输入不同的 URL,如何调用对应的函数?难道写 100 个 elif?

import commons

def run():
    url = input("输入页面URL: ").strip()

    # hasattr:检查模块中是否有这个成员
    if hasattr(commons, url):
        # getattr:动态获取成员
        func = getattr(commons, url)
        func()     # 调用函数
    else:
        print("404 页面不存在")

run()

13.3 反射函数一览

函数作用示例
hasattr(obj, name)判断对象是否有某属性hasattr(commons, "login")
getattr(obj, name)获取对象的属性/方法getattr(commons, "login")
setattr(obj, name, val)设置对象的属性setattr(obj, "name", "value")
delattr(obj, name)删除对象的属性delattr(obj, "name")

13.4 动态导入模块

# __import__() 可以根据字符串动态导入模块
module_name = "os.path"
mod = __import__(module_name)

# 实用场景:根据配置文件动态加载不同的处理模块
handlers = ["handler_login", "handler_dashboard", "handler_report"]
for handler_name in handlers:
    module = __import__(handler_name)
    # 调用模块中的 process 函数
    if hasattr(module, "process"):
        getattr(module, "process")()

思考题:反射在 Web 框架中有什么应用场景?

答:URL 路由!根据用户访问的 URL 字符串,动态找到并调用对应的处理函数,这就是反射最典型的运维/Web 应用。


14. 正则表达式

14.1 为什么需要正则表达式?

类比:正则表达式就像搜索引擎的高级搜索——你不是找一个具体的词,而是找符合某种模式的文本。比如"找出所有手机号"、"找出所有邮箱地址"。

14.2 re 模块基础

import re

text = "服务器IP: 192.168.1.100, 备用IP: 10.0.0.1, 端口: 8080"

# findall:找出所有匹配的内容
ips = re.findall(r'\d+\.\d+\.\d+\.\d+', text)
print(ips)  # ['192.168.1.100', '10.0.0.1']

# search:找到第一个匹配
match = re.search(r'端口: (\d+)', text)
if match:
    print(f"端口号: {match.group(1)}")  # 端口号: 8080

# match:从字符串开头匹配
result = re.match(r'服务器', text)
print(result)  # <re.Match object>(匹配成功)

# sub:替换
cleaned = re.sub(r'\d+', 'X', text)
print(cleaned)
# 服务器IP: X.X.X.X, 备用IP: X.X.X, 端口: X

14.3 常用正则符号

符号含义示例
.匹配任意字符(除换行)a.b 匹配 acb, a3b
\d匹配数字 [0-9]\d{3} 匹配 123
\D匹配非数字\D+ 匹配 abc
\w匹配字母数字下划线\w+ 匹配 hello_123
\s匹配空白字符\s+ 匹配空格、Tab
^匹配行首^hello
$匹配行尾world$
*前一个字符出现0次或多次ab* 匹配 a, ab, abb
+前一个字符出现1次或多次ab+ 匹配 ab, abb
?前一个字符出现0次或1次colou?r 匹配 color, colour
{m,n}前一个字符出现m到n次\d{2,4} 匹配 12, 123, 1234
[]字符集[abc] 匹配 a、b或c
[^]取反[^0-9] 匹配非数字

14.4 实战示例

import re

# 1. 提取日志中的 IP 地址
log_line = '192.168.1.50 - - [01/Jan/2024:10:30:00] "GET /index.html HTTP/1.1" 200'
ip = re.match(r'^(\d+\.\d+\.\d+\.\d+)', log_line)
if ip:
    print(f"访问IP: {ip.group(1)}")
# 输出: 访问IP: 192.168.1.50

# 2. 验证手机号
def is_phone(number):
    pattern = r'^1[3-9]\d{9}$'
    return bool(re.match(pattern, number))

print(is_phone("13812345678"))  # True
print(is_phone("12345"))         # False

# 3. 提取邮箱
text = "联系我: admin@example.com 或 support@test.org"
emails = re.findall(r'[\w.]+@[\w.]+\.\w+', text)
print(emails)  # ['admin@example.com', 'support@test.org']

# 4. 解析 /etc/passwd 格式
passwd_line = "root:x:0:0:root:/root:/bin/bash"
parts = re.split(r':', passwd_line)
print(f"用户: {parts[0]}, UID: {parts[2]}, Shell: {parts[6]}")
# 输出: 用户: root, UID: 0, Shell: /bin/bash

# 5. 编译正则(提升性能,适合重复使用)
ip_pattern = re.compile(r'\d+\.\d+\.\d+\.\d+')
results = ip_pattern.findall("server1: 10.0.0.1, server2: 10.0.0.2")
print(results)  # ['10.0.0.1', '10.0.0.2']

思考题:如何匹配 IP 地址 192.168.1.1 到 192.168.1.255 这个网段的所有 IP?

# 简单版(不严格验证范围)
pattern = r'192\.168\.1\.\d{1,3}'

# 严格版(验证 1-255)
pattern = r'192\.168\.1\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])'

15. 文件 IO 与操作系统交互

15.1 文件读写基础

类比:操作文件就像使用笔记本——先打开(open),然后读或写(read/write),最后合上(close)。

15.2 文件打开模式

模式说明
r只读(默认)
w写入(会清空原文件!)
a追加(在文件末尾添加)
r+读写
w+读写(会清空原文件)
a+读写(追加模式)
b二进制模式(配合其他模式使用,如 rb)

15.3 使用 with 语句(推荐)

问题:如果读取文件时出错了,close() 就不会被执行,文件会一直占用着。

# 不推荐:手动关闭
fo = open("test.txt", "r")
content = fo.read()
fo.close()    # 如果上面出错,这行不会执行

# 推荐:with 语句自动关闭
with open("test.txt", "r", encoding="utf-8") as fo:
    content = fo.read()
    print(content)
# 离开 with 块后自动关闭文件

15.4 文件读写方法

# 读取整个文件
with open("config.txt", "r", encoding="utf-8") as f:
    content = f.read()          # 读取全部内容(字符串)

# 逐行读取
with open("config.txt", "r", encoding="utf-8") as f:
    for line in f:              # 最 Pythonic 的方式
        print(line.strip())

# 读取所有行到列表
with open("config.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()       # 返回列表
    for line in lines:
        print(line.strip())

# 写入文件
with open("output.txt", "w", encoding="utf-8") as f:
    f.write("第一行\n")
    f.write("第二行\n")

# 写入多行
with open("output.txt", "w", encoding="utf-8") as f:
    lines = ["第一行\n", "第二行\n", "第三行\n"]
    f.writelines(lines)

# 追加内容
with open("log.txt", "a", encoding="utf-8") as f:
    f.write("[2024-01-01 10:00:00] 系统启动\n")

15.5 os 模块——操作系统接口

import os

# 当前工作目录
print(os.getcwd())                    # /home/user

# 切换目录
os.chdir("/tmp")

# 创建目录
os.mkdir("test_dir")                  # 创建单级目录
os.makedirs("a/b/c", exist_ok=True)   # 递归创建目录

# 列出目录内容
files = os.listdir(".")
print(files)

# 判断文件/目录
print(os.path.isfile("test.txt"))     # True/False
print(os.path.isdir("test_dir"))      # True/False
print(os.path.exists("/etc/passwd"))  # True

# 路径操作
path = "/home/user/documents/report.txt"
print(os.path.dirname(path))          # /home/user/documents
print(os.path.basename(path))         # report.txt
print(os.path.join("/home", "user", "test.py"))  # /home/user/test.py
print(os.path.splitext("report.txt")) # ('report', '.txt')

# 删除文件和目录
os.remove("test.txt")                 # 删除文件
os.rmdir("empty_dir")                 # 删除空目录

# 环境变量
print(os.environ.get("HOME"))         # /home/user

15.6 shutil 模块——高级文件操作

import shutil

# 复制文件
shutil.copy("source.txt", "backup.txt")          # 复制文件
shutil.copy2("source.txt", "backup2.txt")         # 复制文件(保留元数据)
shutil.copytree("src_dir", "dst_dir")             # 复制整个目录

# 移动/重命名
shutil.move("old_name.txt", "new_name.txt")

# 删除目录树
shutil.rmtree("dir_to_delete")

# 获取磁盘使用情况
total, used, free = shutil.disk_usage("/")
print(f"总计: {total // (1024**3)} GB")
print(f"已用: {used // (1024**3)} GB")
print(f"可用: {free // (1024**3)} GB")

15.7 实战:日志文件分析器

#!/usr/bin/env python3
import re
from collections import Counter

def analyze_log(log_file):
    """分析 Apache/Nginx 访问日志"""
    ip_counter = Counter()
    status_counter = Counter()

    with open(log_file, "r", encoding="utf-8") as f:
        for line in f:
            # 提取IP地址(假设在行首)
            ip_match = re.match(r'^(\d+\.\d+\.\d+\.\d+)', line)
            if ip_match:
                ip_counter[ip_match.group(1)] += 1

            # 提取HTTP状态码
            status_match = re.search(r'" (\d{3}) ', line)
            if status_match:
                status_counter[status_match.group(1)] += 1

    print("=== IP 访问排行 TOP5 ===")
    for ip, count in ip_counter.most_common(5):
        print(f"  {ip}: {count} 次")

    print("\n=== HTTP 状态码统计 ===")
    for status, count in status_counter.most_common():
        print(f"  {status}: {count} 次")

# 使用示例
# analyze_log("/var/log/nginx/access.log")

15.8 目录遍历

import os

# 方法1:os.walk() —— 递归遍历
for root, dirs, files in os.walk("/var/log"):
    for file in files:
        filepath = os.path.join(root, file)
        print(filepath)

# 方法2:查找特定文件
def find_files(directory, extension):
    """在目录中查找指定扩展名的文件"""
    result = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(extension):
                result.append(os.path.join(root, file))
    return result

py_files = find_files("/home/user", ".py")
for f in py_files:
    print(f)

16. 异常处理

16.1 为什么需要异常处理?

类比:异常处理就像汽车的安全气囊——正常行驶时用不到,但出事故时能保命,防止程序直接崩溃。

问题:读取一个可能不存在的配置文件,如果文件不存在程序就崩溃了,怎么办?

16.2 try/except 基本语法

try:
    # 可能出错的代码
    with open("config.ini", "r") as f:
        config = f.read()
except FileNotFoundError:
    # 文件不存在时的处理
    print("配置文件不存在,使用默认配置")
    config = "default_config"
except PermissionError:
    # 权限不足时的处理
    print("权限不足,无法读取配置文件")

16.3 捕获多种异常

try:
    num = int(input("请输入一个数字: "))
    result = 100 / num
    print(f"结果: {result}")
except ValueError:
    print("输入的不是有效数字!")
except ZeroDivisionError:
    print("除数不能为0!")
except Exception as e:
    print(f"未知错误: {e}")

16.4 try/except/else/finally

try:
    f = open("data.txt", "r", encoding="utf-8")
    content = f.read()
except FileNotFoundError:
    print("文件不存在")
except Exception as e:
    print(f"发生错误: {e}")
else:
    # try 块没有异常时执行
    print(f"成功读取 {len(content)} 个字符")
finally:
    # 无论如何都会执行(通常用于资源清理)
    print("清理工作完成")
    if 'f' in dir() and not f.closed:
        f.close()

16.5 raise:主动抛出异常

def set_port(port):
    if not isinstance(port, int):
        raise TypeError("端口号必须是整数")
    if port < 1 or port > 65535:
        raise ValueError(f"端口号 {port} 超出范围(1-65535)")
    print(f"端口设置为: {port}")

# 测试
try:
    set_port(8080)    # OK
    set_port(70000)   # 抛出 ValueError
except (TypeError, ValueError) as e:
    print(f"设置失败: {e}")

16.6 自定义异常

class ServerError(Exception):
    """自定义:服务器错误"""
    pass

class DiskFullError(ServerError):
    """自定义:磁盘已满"""
    pass

def write_log(message):
    import shutil
    _, _, free = shutil.disk_usage("/")
    free_mb = free // (1024 * 1024)
    if free_mb < 100:
        raise DiskFullError(f"磁盘剩余空间不足: {free_mb}MB")
    # 写入日志...
    print(f"日志写入: {message}")

try:
    write_log("系统启动")
except DiskFullError as e:
    print(f"写入失败: {e}")
except ServerError as e:
    print(f"服务器错误: {e}")

16.7 常见异常类型

异常触发场景
FileNotFoundError文件不存在
PermissionError权限不足
ValueError值不合法(如 int("abc"))
TypeError类型错误
KeyError字典键不存在
IndexError列表索引越界
AttributeError属性不存在
ImportError模块导入失败
ZeroDivisionError除以零
KeyboardInterrupt用户按下 Ctrl+C

17. 装饰器

17.1 从闭包说起

类比:闭包就像一个带记忆的函数——它不仅携带了代码逻辑,还"记住"了外部变量的值。

def outer():
    x = 10
    def inner():
        print(f"x = {x}")    # 引用了外部变量 x
    return inner

f = outer()
f()    # 输出: x = 10

闭包的两个条件:

  1. 函数内部定义了另一个函数
  2. 内部函数引用了外部函数的变量(非全局变量)

17.2 装饰器是什么?

类比:装饰器就像给手机贴膜——手机本身没变,但多了一层保护膜(新功能)。

问题:如何给多个函数添加"执行时间统计"功能,又不修改每个函数的代码?

17.3 基本装饰器

import time

# 定义装饰器
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)    # 执行原函数
        end = time.time()
        print(f"[{func.__name__}] 执行耗时: {end-start:.3f}秒")
        return result
    return wrapper

# 使用装饰器
@timer
def slow_function():
    """模拟一个耗时操作"""
    time.sleep(2)
    print("任务完成")

@timer
def fast_function(x, y):
    return x + y

# 调用
slow_function()
# 输出:
# 任务完成
# [slow_function] 执行耗时: 2.001秒

result = fast_function(10, 20)
# 输出: [fast_function] 执行耗时: 0.000秒
print(f"结果: {result}")

17.4 装饰器的执行顺序

# @timer 等价于: slow_function = timer(slow_function)
# 也就是说,decorator 接收原函数作为参数,返回一个新函数

# 带参数的装饰器
def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()
# 输出:
# Hello!
# Hello!
# Hello!

17.5 多装饰器叠加

def deco1(func):
    def wrapper(*args, **kwargs):
        print("[deco1] 开始")
        result = func(*args, **kwargs)
        print("[deco1] 结束")
        return result
    return wrapper

def deco2(func):
    def wrapper(*args, **kwargs):
        print("[deco2] 开始")
        result = func(*args, **kwargs)
        print("[deco2] 结束")
        return result
    return wrapper

@deco1
@deco2
def my_function():
    print("  核心逻辑  ")

my_function()

输出(注意顺序):

[deco1] 开始
[deco2] 开始
  核心逻辑
[deco2] 结束
[deco1] 结束

记忆方法:装饰器从上到下"进入",从下到上"退出",像洋葱一样一层层包裹。

17.6 运维实战:权限检查装饰器

import functools

def require_admin(func):
    """要求管理员权限的装饰器"""
    @functools.wraps(func)    # 保留原函数的文档字符串
    def wrapper(user, *args, **kwargs):
        if user.get("role") != "admin":
            print(f"权限不足:{user['name']} 不是管理员")
            return None
        return func(user, *args, **kwargs)
    return wrapper

@require_admin
def delete_server(user, server_name):
    """删除服务器(需要管理员权限)"""
    print(f"管理员 {user['name']} 删除了服务器 {server_name}")

# 测试
admin = {"name": "张老师", "role": "admin"}
student = {"name": "小明", "role": "student"}

delete_server(admin, "web01")     # 成功删除
delete_server(student, "web02")   # 权限不足

18. 面向对象编程

18.1 什么是面向对象?

类比:面向过程像写菜谱——一步步操作;面向对象像设计一个机器人——把数据(属性)和行为(方法)打包在一起,让机器人自己干活。

  • 类 (Class):蓝图/模板,比如"服务器的设计图"
  • 对象 (Object):根据蓝图造出来的实物,比如"web01这台具体的服务器"

18.2 定义类与创建对象

class Server:
    """服务器类"""

    # 类属性(所有实例共享)
    datacenter = "北京机房"

    def __init__(self, hostname, ip, cpu_cores, memory_gb):
        """构造方法:创建对象时自动调用"""
        # 实例属性(每个实例独有)
        self.hostname = hostname
        self.ip = ip
        self.cpu_cores = cpu_cores
        self.memory_gb = memory_gb
        self.status = "running"

    def show_info(self):
        """显示服务器信息"""
        print(f"主机名: {self.hostname}")
        print(f"IP地址: {self.ip}")
        print(f"CPU核心: {self.cpu_cores}")
        print(f"内存: {self.memory_gb}GB")
        print(f"状态: {self.status}")
        print(f"机房: {self.datacenter}")

    def shutdown(self):
        """关机"""
        self.status = "stopped"
        print(f"{self.hostname} 已关机")

    def start(self):
        """开机"""
        self.status = "running"
        print(f"{self.hostname} 已启动")

# 创建对象(实例化)
web01 = Server("web01", "192.168.1.10", 4, 8)
db01 = Server("db01", "192.168.1.20", 8, 32)

web01.show_info()
print("---")
db01.shutdown()
db01.start()

18.3 封装——保护数据

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner           # 公有属性
        self.__balance = balance     # 私有属性(双下划线)

    def deposit(self, amount):
        """存款"""
        if amount > 0:
            self.__balance += amount
            print(f"存入 {amount} 元,余额: {self.__balance}")
        else:
            print("存款金额必须大于0")

    def get_balance(self):
        """查询余额"""
        return self.__balance

account = BankAccount("张老师", 10000)
account.deposit(5000)               # OK
print(account.get_balance())         # 15000
# print(account.__balance)           # AttributeError! 私有属性不能直接访问

18.4 继承——代码复用

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print(f"{self.name} 发出声音")

class Dog(Animal):
    """Dog 继承自 Animal"""
    def speak(self):
        print(f"{self.name}: 汪汪!")

class Cat(Animal):
    def speak(self):
        print(f"{self.name}: 喵喵!")

# 多态:同一方法,不同表现
animals = [Dog("旺财"), Cat("咪咪"), Dog("大黄")]
for animal in animals:
    animal.speak()
# 输出:
# 旺财: 汪汪!
# 咪咪: 喵喵!
# 大黄: 汪汪!

18.5 运维实战:服务器管理类

class BaseServer:
    """基础服务器类"""
    def __init__(self, hostname, ip):
        self.hostname = hostname
        self.ip = ip
        self.services = []

    def add_service(self, service):
        self.services.append(service)

    def show_status(self):
        print(f"\n{'='*40}")
        print(f"服务器: {self.hostname} ({self.ip})")
        print(f"运行服务: {', '.join(self.services) if self.services else '无'}")

class WebServer(BaseServer):
    """Web 服务器"""
    def __init__(self, hostname, ip, port=80):
        super().__init__(hostname, ip)   # 调用父类构造方法
        self.port = port
        self.add_service(f"nginx:{port}")

    def deploy(self, app_name):
        print(f"在 {self.hostname} 上部署应用: {app_name}")

class DatabaseServer(BaseServer):
    """数据库服务器"""
    def __init__(self, hostname, ip, db_type="MySQL"):
        super().__init__(hostname, ip)
        self.db_type = db_type
        self.add_service(f"{db_type}:3306")

    def backup(self):
        print(f"正在备份 {self.hostname} 的 {self.db_type} 数据...")

# 使用
web = WebServer("web01", "192.168.1.10", 8080)
db = DatabaseServer("db01", "192.168.1.20", "MySQL")

web.deploy("my_app")
web.show_status()

db.backup()
db.show_status()

输出:

在 web01 上部署应用: my_app

========================================
服务器: web01 (192.168.1.10)
运行服务: nginx:8080
正在备份 db01 的 MySQL 数据...

========================================
服务器: db01 (192.168.1.20)
运行服务: MySQL:3306

18.6 类方法与静态方法

class Config:
    _instance = None
    settings = {"debug": False, "port": 8080}

    @classmethod
    def get_setting(cls, key):
        """类方法:可以访问类属性"""
        return cls.settings.get(key)

    @staticmethod
    def validate_port(port):
        """静态方法:不访问类属性,像独立函数"""
        return 1 <= port <= 65535

# 无需实例化即可调用
print(Config.get_setting("port"))       # 8080
print(Config.validate_port(8080))       # True
print(Config.validate_port(99999))      # False

19. MySQL 数据库操作

19.1 环境准备

# 安装 PyMySQL
pip install pymysql

# 安装 MySQL/MariaDB 服务端(Linux)
yum install mariadb mariadb-server -y
systemctl start mariadb

# 创建测试数据库和表
mysql -u root -p
-- MySQL 中执行
CREATE DATABASE IF NOT EXISTS testdb CHARACTER SET utf8mb4;
USE testdb;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    passwd VARCHAR(100) NOT NULL,
    money INT DEFAULT 0
);

INSERT INTO users (name, passwd, money) VALUES
('robin', '12345678', 10000),
('zorro', '87654321', 20000),
('tom', '12345678', 30000);

19.2 连接数据库

import pymysql

# 建立连接
conn = pymysql.connect(
    host='127.0.0.1',
    user='root',
    password='123',
    database='testdb',
    charset='utf8mb4'
)

# 创建游标
cur = conn.cursor()

# ... 执行 SQL ...

# 关闭游标和连接
cur.close()
conn.close()

最佳实践:使用 with 语句确保资源释放:

import pymysql

def get_connection():
    return pymysql.connect(
        host='127.0.0.1',
        user='root',
        password='123',
        database='testdb',
        charset='utf8mb4'
    )

19.3 CRUD 操作

查询 (SELECT)

import pymysql

conn = pymysql.connect(host='127.0.0.1', user='root',
                       password='123', database='testdb')
cur = conn.cursor()

# 查询所有数据
cur.execute("SELECT * FROM users")
all_data = cur.fetchall()
print(f"共 {len(all_data)} 条记录:")
for row in all_data:
    print(f"  ID={row[0]}, 姓名={row[1]}, 余额={row[3]}")

# 查询单条
cur.execute("SELECT * FROM users WHERE name='robin'")
one = cur.fetchone()
print(f"\n查询结果: {one}")

# 查询指定条数
cur.execute("SELECT * FROM users")
two_rows = cur.fetchmany(2)
print(f"\n前两条: {two_rows}")

# 返回字典格式(更易读)
cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
cur.execute("SELECT * FROM users")
for row in cur.fetchall():
    print(f"  {row['name']}: {row['money']} 元")

cur.close()
conn.close()

插入 (INSERT)

import pymysql

conn = pymysql.connect(host='127.0.0.1', user='root',
                       password='123', database='testdb')
cur = conn.cursor()

# 插入单条(使用参数化查询,防止SQL注入!)
sql = "INSERT INTO users (name, passwd, money) VALUES (%s, %s, %s)"
cur.execute(sql, ('jack', '87654321', 50000))
conn.commit()    # 必须提交事务!
print(f"插入成功,新ID: {cur.lastrowid}")

# 批量插入
data = [
    ('lilei', '12345678', 60000),
    ('hanmeimei', '12345678', 70000),
]
cur.executemany(sql, data)
conn.commit()
print(f"批量插入 {cur.rowcount} 条记录")

cur.close()
conn.close()

更新 (UPDATE)

import pymysql

conn = pymysql.connect(host='127.0.0.1', user='root',
                       password='123', database='testdb')
cur = conn.cursor()

sql = "UPDATE users SET money = %s WHERE name = %s"
affected = cur.execute(sql, (99999, 'jack'))
conn.commit()
print(f"更新了 {affected} 条记录")

cur.close()
conn.close()

删除 (DELETE)

import pymysql

conn = pymysql.connect(host='127.0.0.1', user='root',
                       password='123', database='testdb')
cur = conn.cursor()

sql = "DELETE FROM users WHERE name = %s"
affected = cur.execute(sql, ('hanmeimei',))
conn.commit()
print(f"删除了 {affected} 条记录")

cur.close()
conn.close()

19.4 完整实战示例:账户管理系统

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简单的账户管理系统
使用 MySQL 存储数据
"""
import pymysql

def get_conn():
    return pymysql.connect(
        host='127.0.0.1', user='root',
        password='123', database='testdb',
        charset='utf8mb4'
    )

def register(name, passwd, money=0):
    """注册新用户"""
    conn = get_conn()
    cur = conn.cursor()
    try:
        sql = "INSERT INTO users (name, passwd, money) VALUES (%s, %s, %s)"
        cur.execute(sql, (name, passwd, money))
        conn.commit()
        print(f"注册成功!欢迎 {name}")
    except pymysql.err.IntegrityError:
        print("用户名已存在!")
    finally:
        cur.close()
        conn.close()

def login(name, passwd):
    """登录验证"""
    conn = get_conn()
    cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
    sql = "SELECT * FROM users WHERE name=%s AND passwd=%s"
    cur.execute(sql, (name, passwd))
    user = cur.fetchone()
    cur.close()
    conn.close()
    return user

def query_money(name):
    """查询余额"""
    conn = get_conn()
    cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cur.execute("SELECT money FROM users WHERE name=%s", (name,))
    result = cur.fetchone()
    cur.close()
    conn.close()
    if result:
        print(f"{name} 的余额: {result['money']} 元")
    else:
        print("用户不存在")

def update_money(name, amount, operation="add"):
    """充值或消费"""
    conn = get_conn()
    cur = conn.cursor()
    if operation == "add":
        sql = "UPDATE users SET money = money + %s WHERE name = %s"
    else:
        sql = "UPDATE users SET money = money - %s WHERE name = %s"
    cur.execute(sql, (amount, name))
    conn.commit()
    cur.close()
    conn.close()
    action = "充值" if operation == "add" else "消费"
    print(f"{action} {amount} 元成功")

def main():
    while True:
        print("\n===== 账户管理系统 =====")
        print("  (1) 注册")
        print("  (2) 登录")
        print("  (3) 查询余额")
        print("  (4) 充值")
        print("  (5) 消费")
        print("  (0) 退出")

        choice = input("请选择: ").strip()

        if choice == "1":
            name = input("用户名: ")
            passwd = input("密码: ")
            register(name, passwd)
        elif choice == "2":
            name = input("用户名: ")
            passwd = input("密码: ")
            user = login(name, passwd)
            if user:
                print(f"登录成功!余额: {user['money']} 元")
            else:
                print("用户名或密码错误")
        elif choice == "3":
            name = input("用户名: ")
            query_money(name)
        elif choice == "4":
            name = input("用户名: ")
            amount = int(input("充值金额: "))
            update_money(name, amount, "add")
        elif choice == "5":
            name = input("用户名: ")
            amount = int(input("消费金额: "))
            update_money(name, amount, "sub")
        elif choice == "0":
            print("再见!")
            break
        else:
            print("无效选项")

if __name__ == "__main__":
    main()

安全提醒:永远使用参数化查询(%s 占位符),不要拼接 SQL 字符串,以防止 SQL 注入攻击!


附录:常用内置函数速查表

函数说明示例
print()输出print("hello")
input()获取用户输入name = input("姓名: ")
int()转整数int("123")
float()转浮点数float("3.14")
str()转字符串str(100)
type()查看类型type(123)
len()求长度len("hello")
range()生成整数序列range(1, 10)
enumerate()带索引遍历enumerate(["a","b"])
sorted()排序sorted([3,1,2])
reversed()反转reversed([1,2,3])
sum()求和sum([1,2,3])
max()最大值max([5,3,8])
min()最小值min([5,3,8])
abs()绝对值abs(-5)
round()四舍五入round(3.14159, 2)
id()对象内存地址id(x)
isinstance()类型检查isinstance(1, int)
open()打开文件open("f.txt", "r")
dir()查看对象属性dir(str)
help()查看帮助help(len)

综合练习

练习 1:系统信息采集脚本

编写一个 Python 脚本,采集以下信息并输出为 JSON 格式:

  • 主机名
  • 当前时间
  • 磁盘使用情况
  • Python 版本

练习 2:批量文件处理

在指定目录下随机生成 100 个文件,要求:

  • 文件名格式:data_001.txt ~ data_100.txt
  • 每个文件写入随机内容(1-100行)
  • 统计所有文件的总大小

练习 3:日志分析工具

读取 Nginx 访问日志,统计:

  • 访问量最高的 IP(Top 10)
  • 各 HTTP 状态码的数量
  • 每小时的访问量分布

练习 4:猜数字游戏

程序随机生成一个 1-100 的整数
用户有 7 次机会猜
每次猜完提示"大了"或"小了"
猜中则恭喜用户并显示猜了几次

学习建议:编程就像学数学——看懂不等于会做,一定要动手敲代码。建议每学完一个章节就完成对应的练习题,遇到问题先自己查文档、看报错信息,实在解决不了再请教老师。祝学习愉快!

默认分类
python
License:  CC BY 4.0
Share

Further Reading

Jul 4, 2026

Nginx与Tomcat

Nginx 与 Tomcat 完全指南 目录 第一部分:Nginx 篇 1. Nginx 介绍 -- 你的网站为什么需要一位"前台接待员"?

Jul 4, 2026

网络协议与安全

网络协议与安全 -- 从入门到实战 目录 IP地址 -- 网络世界的门牌号 TCP/IP协议 -- 数据快递的工作流程 iptables防火墙

Jul 4, 2026

数据库与自动化运维

第四篇:数据库与自动化运维 本篇从"为什么需要数据库"这个根本问题出发,逐步带你掌握 MySQL 关系型数据库、Redis 内存数据库,以及自动化运维的核心技能。每一个知识点都遵循"遇到问题 -> 分析原因 -> 动手解决"的思路。 目录 第一部分:MySQL 数据库 数据库介绍 | 2. MySQ

OLDER

数据库与自动化运维

NEWER

Shell脚本与版本控制

Recently Updated

  • 监控虚拟化与集群
  • Docker与Kubernetes
  • Nginx与Tomcat
  • 网络协议与安全
  • 数据库与自动化运维

Trending Tags

安全 Linux系统 nginx 日志管理 Linux服务 网络 Linux高级 pxe 中间件 python

Contents

©2026 青青子衿的拾枝杂谈 . Some rights reserved.

Using the Halo theme Chirpy