Skip to content

基础语法

Python 基础语法覆盖变量、类型、条件、循环、函数、异常和退出码。运维脚本经常处理命令输出、JSON、接口响应和批量主机列表,这些内容都建立在基础语法上。

Python 代码最明显的特点是缩进。缩进不是排版习惯,而是语法结构的一部分。iffordef 下面的代码块都靠缩进表示归属。

一、变量与类型

Python 不需要声明类型,赋值即定义。

python
service_name = "nginx"   # str,字符串
port = 80                # int,整数
load_avg = 0.78          # float,浮点数
enabled = True           # bool,布尔值
last_error = None        # NoneType,表示没有值

type() 可以查看变量当前类型:

python
print(type(port))        # <class 'int'>

变量名通常用小写加下划线,例如 service_namemax_retry。脚本跑起来以后,变量名就是排查时的上下文,名字清楚比缩写省下来的几个字符更重要。

常见类型:

类型示例运维场景
str"mysql"服务名、路径、命令输出
int3306端口、次数、状态码
float0.95CPU 使用率、延迟
boolTrue / False开关、检查结果
NoneNone没有查到值、默认空值

二、字符串

运维脚本里字符串用得很多:路径、命令、日志、URL、错误信息都属于字符串。

2.1 f-string

f-string 可以把变量嵌进字符串:

python
service = "nginx"
port = 80

message = f"{service} listens on port {port}"
print(message)  # nginx listens on port 80

花括号里可以写简单表达式:

python
used = 8589934592
print(f"used={used // (1024 ** 3)}GB")  # used=8GB

格式化小数:

python
cpu_usage = 83.126
print(f"cpu={cpu_usage:.2f}%")  # cpu=83.13%

2.2 常用方法

python
line = "  ERROR disk full  "

clean_line = line.strip()              # 去掉首尾空白
level = clean_line.split()[0]          # 按空白切分,取第一个字段
lower_line = clean_line.lower()        # 转小写,便于统一比较
has_error = clean_line.startswith("ERROR")

print(level, lower_line, has_error)

几个方法的用途:

方法用途
strip()去首尾空白和换行
split()切分日志行、命令输出
replace()替换字符串
startswith()判断前缀
endswith()判断后缀
lower() / upper()统一大小写

2.3 多行字符串

三引号保留换行,适合写多行配置模板或测试日志:

python
config = """server {
    listen 80;
    server_name example.com;
}
"""

print(config)

三、运算符

基础运算:

python
print(10 + 3)     # 13
print(10 - 3)     # 7
print(10 * 3)     # 30
print(10 / 3)     # 3.333...
print(10 // 3)    # 3,整除
print(10 % 3)     # 1,取余
print(2 ** 3)     # 8,幂运算

比较和逻辑:

python
cpu_usage = 85
disk_usage = 70

print(cpu_usage >= 80)                         # True
print(cpu_usage >= 80 and disk_usage >= 80)    # False
print(cpu_usage >= 80 or disk_usage >= 80)     # True
print(not cpu_usage >= 80)                     # False

成员判断常用于列表、字符串和字典:

python
services = ["nginx", "mysql", "redis"]

print("nginx" in services)       # True
print("php" not in services)     # True

四、条件判断

if / elif / else 处理分支:

python
cpu_usage = 85

if cpu_usage >= 90:
    status = "critical"
elif cpu_usage >= 70:
    status = "warning"
else:
    status = "ok"

print(f"cpu={cpu_usage}% status={status}")

Python 里这些值会被当作 False

说明
0数字零
""空字符串
[]空列表
{}空字典
None没有值
False布尔假

示例:

python
result = ""

if not result:
    print("result is empty")

三元表达式适合很短的判断:

python
level = "high" if cpu_usage >= 80 else "normal"

复杂条件写成普通 if 更清楚。脚本不是为了压行数,排查时能一眼看出分支更重要。

五、循环

5.1 for 循环

遍历列表:

python
services = ["nginx", "mysql", "redis"]

for service in services:
    print(f"checking {service}")

遍历字典:

python
ports = {
    "nginx": 80,
    "mysql": 3306,
    "redis": 6379,
}

for service, port in ports.items():
    print(f"{service} -> {port}")

带序号遍历:

python
for index, service in enumerate(services, start=1):
    print(f"{index}. {service}")

range() 生成数字序列:

python
for retry in range(1, 4):
    print(f"attempt {retry}")

5.2 while 循环

while 适合重试、轮询这类次数不完全固定的逻辑:

python
import time

retry = 0
max_retry = 3

while retry < max_retry:
    print(f"attempt={retry + 1}")
    retry += 1
    time.sleep(1)  # 重试之间留一点间隔,避免打满接口或服务

5.3 break 和 continue

break 结束整个循环,continue 跳过本次循环。

python
services = ["nginx", "mysql", "redis"]

for service in services:
    if service == "mysql":
        continue  # mysql 交给专门脚本检查

    print(f"checking {service}")
python
for service in services:
    if service == "mysql":
        print("found mysql")
        break  # 找到目标后结束循环

六、函数

函数把一段逻辑命名。脚本变长以后,函数能减少重复,也方便单独排查。

python
def format_status(service, status):
    # 返回格式统一的状态文本,调用方负责 print 或写日志
    return f"service={service} status={status}"


message = format_status("nginx", "ok")
print(message)

检查端口示例:

python
import socket


def check_port(host, port, timeout=2):
    """检查 TCP 端口是否能连通。"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(timeout)

    try:
        result = sock.connect_ex((host, port))
        return result == 0
    finally:
        sock.close()  # socket 属于系统资源,用完及时关闭


if check_port("127.0.0.1", 22):
    print("ssh is reachable")

默认参数:

python
def run_check(name, timeout=30, retries=3):
    print(f"name={name} timeout={timeout} retries={retries}")


run_check("disk")
run_check("api", timeout=10)

返回多个值时,本质是返回元组:

python
def parse_target(target):
    host, port_text = target.split(":")
    return host, int(port_text)


host, port = parse_target("127.0.0.1:22")
print(host, port)

七、异常处理

异常表示运行时出错。文件不存在、接口超时、字符串转数字失败、命令返回非零,都可能变成异常。脚本里完全不处理异常,失败时只留一大段堆栈;处理过度又会把真正错误吞掉。比较稳的方式是:在边界处捕获,打印清楚上下文,再返回失败退出码。

python
try:
    value = int("abc")
except ValueError as exc:
    print(f"parse integer failed: {exc}")

文件读取:

python
from pathlib import Path

path = Path("/etc/hosts")

try:
    content = path.read_text(encoding="utf-8")
except FileNotFoundError:
    print(f"file not found: {path}")
else:
    print(content.splitlines()[0])

finally 适合清理资源:

python
lock_file = open("/tmp/demo.lock", "w", encoding="utf-8")

try:
    lock_file.write("running\n")
finally:
    lock_file.close()  # 不管前面是否报错,都关闭文件句柄

主动抛异常:

python
def validate_port(port):
    if not 1 <= port <= 65535:
        raise ValueError(f"invalid port: {port}")

八、退出码

Shell、cron、systemd、CI 都通过退出码判断脚本是否成功。0 表示成功,非零表示失败。

python
import sys


def main():
    service_ok = False

    if not service_ok:
        print("service check failed", file=sys.stderr)
        return 1

    print("service check ok")
    return 0


if __name__ == "__main__":
    sys.exit(main())

运行后查看退出码:

bash
uv run python check_service.py
echo $?

main() 写成返回数字,再统一 sys.exit(main()),分支多的时候比到处写 sys.exit() 更清楚。

九、注释和文档字符串

注释写“为什么这样处理”或“这个值有什么背景”,不重复代码本身。

python
# systemd 的 ActiveState 只表示单元状态,不代表 HTTP 接口一定可用
active_state = "active"

函数文档字符串写输入、输出和关键限制:

python
def parse_log_line(line):
    """解析一行日志,返回 (time, level, message),格式不匹配时返回 None。"""
    parts = line.strip().split(maxsplit=2)
    if len(parts) != 3:
        return None
    return parts[0], parts[1], parts[2]

运维脚本常常隔几个月才回头改一次。关键分支、危险操作、外部依赖和退出码位置有注释,后面排查会轻松很多。