从零到接单 05:函数与模块——把代码组织成可复用的零件

pythontutorialbeginner自动化爬虫接单函数模块

系列目录:本文是「从零到接单:Python 自动化与爬虫实战」系列的第 5 篇。前面的代码都是从头写到尾,但真实项目可能有几百上千行代码,全靠顺序执行会乱成一团。这篇我们学习函数和模块——把大任务拆成小零件,这是写大程序的基本功。


一、什么是函数?为什么要用?

你已经用过很多内置函数了:print()input()len()type()……它们帮你完成了特定任务。

现在你要学会自己写函数。函数的作用:

  1. 避免重复:同样的逻辑不用写多次
  2. 让代码清晰:给一段代码起个有意义的名字
  3. 方便维护:改一处,所有调用都生效
  4. 别人也能用:函数可以分享给其他人

二、定义和调用函数

基本语法

def 函数名(参数1, 参数2, ...):
    """文档字符串:说明这个函数做什么(可选但推荐)"""
    函数体
    return 返回值  # 可选

第一个函数

def greet(name):
    """向指定用户打招呼"""
    return f"你好,{name}!欢迎回来。"

# 调用
result = greet("小明")
print(result)  # 你好,小明!欢迎回来。

print(greet("小红"))  # 你好,小红!欢迎回来。

无返回值的函数

def print_separator(char="-", length=40):
    """打印分隔线"""
    print(char * length)

print_separator()         # ----------------------------------------
print_separator("=", 20)  # ====================
print("程序结束")
print_separator("~")      # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

实际返回了 None(Python 的空值)。


三、参数的类型

位置参数

def introduce(name, age, city):
    print(f"我叫{name},{age}岁,来自{city}")

introduce("小明", 25, "北京")  # 按位置传参

默认参数(最常用!)

def introduce(name, age, city="北京"):
    print(f"我叫{name},{age}岁,来自{city}")

introduce("小明", 25)           # 我叫小明,25岁,来自北京
introduce("小红", 22, "上海")   # 我叫小红,22岁,来自上海

⚠️ 有默认值的参数必须放在没有默认值的参数后面

关键字参数

def order_product(name, price, quantity=1):
    total = price * quantity
    print(f"商品:{name},单价:{price},数量:{quantity},总价:{total}")

# 用关键字可以不按顺序
order_product(quantity=3, name="Python书", price=59)
# 商品:Python书,单价:59,数量:3,总价:177

不定长参数:*args 和 **kwargs

# *args:接收任意数量的位置参数(打包成元组)
def sum_all(*numbers):
    """求和任意数量的数"""
    return sum(numbers)

print(sum_all(1, 2, 3))         # 6
print(sum_all(1, 2, 3, 4, 5))   # 15

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

print_info(name="小明", age=25, city="北京")
# name: 小明
# age: 25
# city: 北京

四、返回值

返回单个值

def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 8

返回多个值(实际是返回元组)

def calculate(a, b):
    return a + b, a - b, a * b, a / b

add_result, sub_result, mul_result, div_result = calculate(10, 5)
print(f"加:{add_result},减:{sub_result},乘:{mul_result},除:{div_result}")
# 加:15,减:5,乘:50,除:2.0

提前返回

def divide(a, b):
    if b == 0:
        print("错误:除数不能为 0")
        return  # 提前结束函数
    return a / b

print(divide(10, 0))  # None
print(divide(10, 2))  # 5.0

五、作用域:变量在哪里可见

x = "全局变量"

def test():
    y = "局部变量"
    print(x)        # 可以访问全局变量
    print(y)        # 可以访问局部变量

test()
print(x)            # ✅ 可以
# print(y)          # ❌ NameError: y 不存在于全局作用域

global 关键字(谨慎使用)

count = 0

def increment():
    global count   # 声明要修改全局变量
    count += 1

increment()
print(count)       # 1

更好的做法是用返回值,避免全局变量:

def increment(count):
    return count + 1

count = 0
count = increment(count)

六、lambda 表达式:匿名函数

一行就能搞定的简单函数,不需要用 def 正式定义:

# 普通写法
def add(a, b):
    return a + b

# lambda 写法
add = lambda a, b: a + b

print(add(3, 5))  # 8

# lambda 常见用法:作为 sorted/key 参数
students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78},
]

# 按分数排序
sorted_students = sorted(students, key=lambda s: s["score"], reverse=True)
for s in sorted_students:
    print(f"{s['name']}: {s['score']}")
# 李四: 92
# 张三: 85
# 王五: 78

七、模块(Module):把函数组织到不同文件

当代码越来越多,需要分文件管理。

创建模块

新建 utils.py

# utils.py

def read_csv(filepath):
    """读取 CSV 文件"""
    with open(filepath, "r", encoding="utf-8") as f:
        lines = f.readlines()
    headers = lines[0].strip().split(",")
    data = []
    for line in lines[1:]:
        values = line.strip().split(",")
        data.append(dict(zip(headers, values)))
    return data

def write_csv(filepath, data):
    """写入 CSV 文件"""
    if not data:
        return
    headers = list(data[0].keys())
    with open(filepath, "w", encoding="utf-8") as f:
        f.write(",".join(headers) + "\n")
        for row in data:
            f.write(",".join(str(row[h]) for h in headers) + "\n")

VERSION = "1.0.0"
AUTHOR = "小明"

导入模块

# main.py

# 方式一:import 模块名
import utils
data = utils.read_csv("data.csv")

# 方式二:from 模块 import 特定内容
from utils import read_csv, write_csv
data = read_csv("data.csv")

# 方式三:from 模块 import *(不推荐,会污染命名空间)
# from utils import *

# 方式四:起别名
import utils as u
data = u.read_csv("data.csv")

常用标准库模块预览

import os           # 操作系统接口(文件路径、环境变量)
import sys          # 系统参数(命令行参数)
import json         # JSON 处理
import csv          # CSV 文件处理
import re           # 正则表达式
import datetime     # 日期时间
import random       # 随机数
import time         # 时间相关
import math         # 数学函数

if name == "main"

# utils.py 底部加这个
if __name__ == "__main__":
    # 这段代码只在直接运行 utils.py 时执行
    # 被其他文件 import 时不会执行
    print("测试 utils 模块...")
    test_data = [{"name": "测试", "age": "25"}]
    write_csv("test.csv", test_data)
    print(read_csv("test.csv"))

八、实战练习:爬虫工具模块

提前写一个后面会用到的小工具模块:

# scraper_utils.py —— 爬虫常用工具函数

import time
import random

def clean_text(text):
    """清洗文本:去掉首尾空格和多余空白"""
    if not text:
        return ""
    return " ".join(text.split())

def extract_number(text):
    """从文本中提取数字"""
    import re
    numbers = re.findall(r"\d+\.?\d*", text)
    return float(numbers[0]) if numbers else None

def safe_get(data, *keys, default=None):
    """安全地从嵌套字典中取值"""
    for key in keys:
        if isinstance(data, dict):
            data = data.get(key, default)
        else:
            return default
    return data

def random_delay(min_seconds=0.5, max_seconds=2.0):
    """随机延时,避免被封(爬虫必备)"""
    delay = random.uniform(min_seconds, max_seconds)
    time.sleep(delay)

def retry(max_attempts=3, delay=1):
    """装饰器:失败自动重试"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise e
                    print(f"第 {attempt + 1} 次失败,{delay} 秒后重试...")
                    time.sleep(delay)
        return wrapper
    return decorator

if __name__ == "__main__":
    # 测试
    print(clean_text("  你好   世界  "))         # 你好 世界
    print(extract_number("价格:99.9元"))        # 99.9
    data = {"a": {"b": {"c": "hello"}}}
    print(safe_get(data, "a", "b", "c"))         # hello
    print(safe_get(data, "a", "x", default="无")) # 无
    print("所有工具函数测试通过 ✅")

总结

| 概念 | 写法 | 要点 | |------|------|------| | 定义函数 | def func(params): | 名字清晰、参数合理 | | 返回值 | return value | 可返回多个值(元组) | | 默认参数 | def f(a, b=0) | 有默认值的往后放 | | *args | def f(*args) | 任意数量位置参数 | | **kwargs | def f(**kwargs) | 任意数量关键字参数 | | lambda | lambda x: x+1 | 一行函数 | | 导入模块 | import module | 用 . 访问 | | 条件执行 | if __name__ == "__main__" | 只在直接运行时执行 |

下一篇,我们将学习文件读写与异常处理——程序终于可以和外部世界交互了!


练习:写一个 password_tool.py 模块,包含以下函数:

  1. generate_password(length=12) —— 生成随机密码(含大小写字母+数字+符号)
  2. check_strength(password) —— 检查密码强度(弱/中/强)
  3. if __name__ == "__main__" 中测试这两个函数

Comments

Sign in to leave a comment.