系列文章

Python 从入门到精通

第 7 / 36 篇

从环境安装和基础语法出发,逐步学习工程实践、自动化、数据处理与 Web 开发。

  1. 01
    Python 怎么装、怎么用
  2. 02
    变量、数字和字符串
  3. 03
    列表:把一组数据放在一起
  4. 04
    元组、集合和字典
  5. 05
    条件判断:让程序做选择
  6. 06
    循环:重复的事交给程序
  7. 07
    函数:把代码整理成可复用的块正在阅读
  8. 08
    模块与包:拆分你的程序
  9. 09
    输入、输出与字符串格式化
  10. 10
    文件读写:保存程序的数据
  11. 11
    异常处理:程序出错时怎么办
  12. 12
    基础阶段练习:命令行记账本
  13. 13
    类和对象:面向对象入门
  14. 14
    继承、组合与特殊方法
  15. 15
    迭代器与生成器
  16. 16
    列表推导式与生成器表达式
  17. 17
    装饰器:给函数增加能力
  18. 18
    上下文管理器与 with
  19. 19
    类型标注与 dataclass
  20. 20
    正则表达式:从文本中找规律
  21. 21
    日期、时间与时区
  22. 22
    日志与调试
  23. 23
    虚拟环境与依赖管理
  24. 24
    测试:让修改不再提心吊胆
  25. 25
    网络请求:用 Python 调用 API
  26. 26
    网页解析与合规采集
  27. 27
    操作 Excel、CSV 与批量文件
  28. 28
    SQLite:给程序加一个数据库
  29. 29
    数据分析入门:NumPy 与 Pandas
  30. 30
    画图:把数据变得直观
  31. 31
    Flask 入门:做一个小网站
  32. 32
    异步编程:同时处理多项任务
  33. 33
    线程、进程与并发选择
  34. 34
    性能分析与优化
  35. 35
    项目结构、配置与发布
  36. 36
    综合项目:从需求到上线

查看整个系列 →

写了这么多行代码,你可能已经发现一个问题:同样的逻辑(比如”算平均分”)要在好几个地方用,就得复制粘贴好几遍。复制粘贴一时爽,改需求火葬场——改一处漏一处。解决之道就是函数:把一段代码打包,起个名字,随用随调。

定义和调用

def greet(name):
    print(f"你好,{name}!")

greet("小明")   # 你好,小明!
greet("小红")   # 你好,小红!

def 定义函数,后面跟函数名和括号,冒号下面是函数体(记得缩进)。函数不调用就不会执行,调用就写函数名加括号。

参数:函数的数据入口

函数可以接收参数,参数在括号里声明:

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

print(add(3, 5))       # 8(位置参数)
print(add(a=1, b=2))   # 3(关键字参数,更明确)

参数可以带默认值,调用时可以省略:

def greet(name, greeting="你好"):
    print(f"{greeting},{name}!")

greet("小明")              # 你好,小明!
greet("小红", "早上好")     # 早上好,小红!

注意规则:默认值参数必须放在非默认参数后面。写成 def f(a=1, b) 会报语法错误。

return:函数的输出

函数处理完数据,用 return 把结果交回去:

def average(scores):
    return sum(scores) / len(scores)

avg = average([92, 85, 78, 100])
print(f"平均分:{avg:.1f}")   # 平均分:88.8

几个要点:

  • return 后面的代码不会执行,函数到 return 就结束了
  • 没有 return 的函数返回 None(比如只打印的函数)
  • 可以返回多个值,用逗号分隔,本质是返回元组:return a, b
def min_max(nums):
    return min(nums), max(nums)

lo, hi = min_max([3, 1, 4, 1, 5])
print(lo, hi)   # 1 5(解包接收多返回值)

*args 和 **kwargs:不确定多少个参数

有时候不知道调用方会传几个参数,用 *args 收位置参数(打包成元组)、**kwargs 收关键字参数(打包成字典):

def log(level, *messages):
    for msg in messages:
        print(f"[{level}] {msg}")

log("INFO", "启动", "加载配置", "连接数据库")
# [INFO] 启动
# [INFO] 加载配置
# [INFO] 连接数据库

def show_info(**info):
    for key, value in info.items():
        print(f"{key}: {value}")

show_info(name="南山", year=2026)
# name: 南山
# year: 2026

名字 args/kwargs 是约定俗成,星号才是关键。

作用域:函数里的变量是”本地人”

x = 10          # 全局变量

def change():
    x = 20      # 这是新的局部变量,和外面的 x 无关!
    print(x)    # 20

change()
print(x)        # 10(外面的没变)

函数里赋值默认创建局部变量,函数结束就销毁。想改全局变量要用 global 声明,但尽量别用——全局变量多了,程序的状态就难追踪了。正确做法是把值传进函数、用 return 传出来:

def add_one(x):
    return x + 1

x = 10
x = add_one(x)   # 显式更新
print(x)         # 11

文档字符串:给函数写说明书

def area(radius):
    """计算圆的面积。

    参数 radius: 半径(数字)
    返回: 面积
    """
    return 3.14159 * radius ** 2

print(area.__doc__)  # 能看到说明
help(area)           # 交互环境里也能看

函数第一行的三引号字符串就是文档字符串,好的习惯从第一篇就养成。

新手坑

坑 1:忘记 return。函数算完没 return,外面拿到的是 None,再往下算就报错:TypeError: unsupported operand type(s)

坑 2:默认参数用可变对象。

def add_item(item, items=[]):   # 危险!默认列表只创建一次
    items.append(item)
    return items

print(add_item("a"))  # ['a']
print(add_item("b"))  # ['a', 'b'] ??上次的还在!

默认参数在定义时只创建一次,之后每次调用都复用。要安全就用 None:

def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

坑 3:函数名和变量名冲突。后面定义的同名变量会覆盖函数,调用的就是变量了。

小结

  • def 函数名(参数): 定义,函数名 + 括号调用
  • 参数支持默认值(放后面)、*args**kwargs
  • return 返回结果(可多值),没有 return 返回 None
  • 函数内变量默认是局部的;少用 global,多用传参 + return
  • 写文档字符串;默认参数别用可变对象

练习

  1. 写一个函数 fahrenheit(c),把摄氏温度转成华氏(公式:F = C × 9/5 + 32)
  2. 写一个函数 count_chars(s),统计字符串里每个字符出现次数,返回字典
  3. 思考:下面的代码输出什么,为什么?
    def f():
        return 1
        print("这行会执行吗?")
    print(f())