【Python从入门到精通】第 006 篇:函数——Python 编程的核心抽象
Python函数:从基础到高阶用法 本文深入讲解Python函数的核心概念与应用技巧。函数作为代码复用的基本单元,其灵活的参数机制和丰富特性是Python编程的关键。主要内容包括: 函数基础:定义与调用语法、命名规范,通过计算圆面积案例展示函数消除重复代码的价值。 参数详解:四种参数类型: 位置参数(顺序敏感) 关键字参数(显式命名) 默认参数(注意可变默认值的陷阱) 可变参数(*args和**k
上一篇:【第 005 篇】流程控制——条件判断与循环让程序活起来
下一篇:【第 007 篇】Python内置数据结构:列表、元组、字典、集合全解析
系列说明:本系列共 30 篇,全面介绍 Python 编程从零基础到软件工程师的完整路径。本文为第 006 篇,深入讲解 Python 函数的方方面面——从基础定义到高阶用法,函数是软件工程的基石,也是代码复用的核心机制。
摘要
如果说变量是存放数据的容器,那么函数就是封装逻辑的容器。一个没有函数的程序是一大块"意大利面条式"的代码,难以阅读、维护和复用。Python 的函数功能非常强大——支持多种参数类型、闭包、lambda、高阶函数等——掌握函数是走向 Python 进阶的关键一步。
本文涵盖:
- 函数的定义与调用
- 四种参数类型:位置参数、关键字参数、默认参数、可变参数
- 返回值:
return与多值返回 - 变量作用域:LEGB 规则
lambda匿名函数- 高阶函数:
map()、filter()、sorted()的函数式用法 - 递归函数与递归陷阱
- 函数注解(类型提示)
- 综合实战:实现一个命令行计算器
一、函数基础
1.1 为什么需要函数
设想你需要在代码中多次计算圆的面积:
# 没有函数的写法(重复代码)
import math
radius1 = 5
area1 = math.pi * radius1 ** 2
print(f"圆1面积:{area1:.2f}")
radius2 = 3
area2 = math.pi * radius2 ** 2
print(f"圆2面积:{area2:.2f}")
radius3 = 7
area3 = math.pi * radius3 ** 2
print(f"圆3面积:{area3:.2f}")
# 使用函数的写法(简洁、可维护)
import math
def circle_area(radius):
"""计算圆的面积。"""
return math.pi * radius ** 2
for r in [5, 3, 7]:
print(f"半径 {r} 的圆面积:{circle_area(r):.2f}")
函数解决了以下问题:
- 消除重复代码(DRY 原则:Don’t Repeat Yourself)
- 提高可读性:有意义的函数名让代码自解释
- 易于维护:只需修改函数定义,所有调用处自动更新
- 方便测试:可以单独测试每个函数
1.2 函数定义与调用
# 基本语法
def 函数名(参数列表):
"""文档字符串(可选)"""
函数体
return 返回值 # 可选
# 示例
def greet(name):
"""向指定的人打招呼。"""
message = f"你好,{name}!"
return message
# 调用函数
result = greet("Alice")
print(result) # 你好,Alice!
# 也可以直接使用返回值
print(greet("Bob")) # 你好,Bob!
函数命名规则(遵循 PEP 8):
# 好的函数名:动词 + 名词,描述动作
def calculate_total_price():
pass
def validate_user_input():
pass
def send_email_notification():
pass
# 不好的函数名
def func1():
pass
def doStuff(): # 应该是 do_stuff
pass
二、参数详解
Python 函数支持四种参数类型,灵活性极强。
2.1 位置参数(Positional Arguments)
def describe_person(name, age, city):
print(f"{name},{age}岁,来自{city}")
# 按位置传入(顺序必须匹配)
describe_person("Alice", 30, "北京")
describe_person("Bob", 25, "上海")
2.2 关键字参数(Keyword Arguments)
def describe_person(name, age, city):
print(f"{name},{age}岁,来自{city}")
# 按关键字传入(顺序不重要)
describe_person(age=30, city="北京", name="Alice")
# 位置参数和关键字参数可以混用(位置参数必须在前)
describe_person("Alice", city="北京", age=30)
2.3 默认参数(Default Arguments)
def greet(name, greeting="你好", punctuation="!"):
"""打招呼,可以自定义问候语。"""
print(f"{greeting},{name}{punctuation}")
# 使用默认值
greet("Alice") # 你好,Alice!
greet("Bob", greeting="早上好") # 早上好,Bob!
greet("Charlie", "晚上好", "~") # 晚上好,Charlie~
默认参数的陷阱:可变默认值
# 错误写法!默认列表在所有调用中共享
def add_item_wrong(item, lst=[]):
lst.append(item)
return lst
print(add_item_wrong(1)) # [1]
print(add_item_wrong(2)) # [1, 2] ← 不对!列表没有重置
print(add_item_wrong(3)) # [1, 2, 3] ← 继续累积!
# 正确写法:用 None 作默认值,函数内部创建新列表
def add_item_correct(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
print(add_item_correct(1)) # [1]
print(add_item_correct(2)) # [2] ← 每次都是新列表
规则:永远不要用可变对象(
list、dict、set)作为默认参数,应使用None。
2.4 可变参数(Variable Arguments)
*args:接收任意数量的位置参数
def sum_all(*args):
"""计算所有参数的和。"""
print(f"参数类型:{type(args)}") # tuple
total = sum(args)
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0
# 混合使用
def log_message(level, *messages):
for msg in messages:
print(f"[{level}] {msg}")
log_message("INFO", "服务启动", "监听端口 8080")
# [INFO] 服务启动
# [INFO] 监听端口 8080
**kwargs:接收任意数量的关键字参数
def create_user(**kwargs):
"""创建用户,支持任意属性。"""
print(f"参数类型:{type(kwargs)}") # dict
for key, value in kwargs.items():
print(f" {key}: {value}")
create_user(name="Alice", age=30, city="北京", role="admin")
# 参数类型:<class 'dict'>
# name: Alice
# age: 30
# city: 北京
# role: admin
# 混合使用(参数顺序:位置参数 → *args → 关键字参数 → **kwargs)
def mixed_function(required, *args, keyword_only=True, **kwargs):
print(f"required: {required}")
print(f"args: {args}")
print(f"keyword_only: {keyword_only}")
print(f"kwargs: {kwargs}")
mixed_function("必须", 1, 2, 3, keyword_only=False, extra="补充")
参数解包:将列表/字典作为参数传入
def add(a, b, c):
return a + b + c
nums = [1, 2, 3]
print(add(*nums)) # 解包列表,等价于 add(1, 2, 3)
params = {"a": 10, "b": 20, "c": 30}
print(add(**params)) # 解包字典,等价于 add(a=10, b=20, c=30)
三、返回值
3.1 return 语句
def add(a, b):
return a + b
# 没有 return,默认返回 None
def print_hello():
print("Hello")
result = print_hello()
print(result) # None
# 可以提前 return(函数终止)
def is_positive(n):
if n <= 0:
return False # 提前返回
# 继续执行
print(f"{n} 是正数")
return True
3.2 多值返回(返回元组)
# Python 函数可以同时返回多个值(实际上是返回一个元组)
def min_max(numbers):
"""返回列表的最小值和最大值。"""
return min(numbers), max(numbers)
result = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(result) # (1, 9)
print(type(result)) # <class 'tuple'>
# 解包接收
minimum, maximum = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(f"最小值:{minimum},最大值:{maximum}")
# 实际应用:统计函数
def analyze_scores(scores):
"""分析成绩列表,返回多个统计指标。"""
if not scores:
return None, None, None, None
return (
min(scores),
max(scores),
sum(scores) / len(scores),
len(scores)
)
scores = [85, 92, 78, 96, 88, 73]
low, high, avg, count = analyze_scores(scores)
print(f"最低分:{low},最高分:{high},平均分:{avg:.1f},共 {count} 人")
四、变量作用域:LEGB 规则
4.1 什么是作用域
变量的**作用域(scope)**决定了在哪里可以访问这个变量。Python 按照 LEGB 规则查找变量:
- L(Local):函数内部的局部变量
- E(Enclosing):嵌套函数的外层函数
- G(Global):模块级别的全局变量
- B(Built-in):Python 内置名称(如
print、len)
x = "全局" # Global
def outer():
x = "外层" # Enclosing
def inner():
x = "内层" # Local
print(x) # 打印 Local 的 x
inner()
print(x) # 打印 Enclosing 的 x
outer()
print(x) # 打印 Global 的 x
# 输出:内层 → 外层 → 全局
4.2 global 与 nonlocal 关键字
# global:在函数内修改全局变量
count = 0
def increment():
global count # 声明使用全局变量
count += 1
increment()
increment()
print(count) # 2
# 不用 global 的话,函数内部会创建新的局部变量
def wrong_increment():
count += 1 # UnboundLocalError!
# nonlocal:在内层函数中修改外层函数的变量
def make_counter():
count = 0
def counter():
nonlocal count # 声明使用外层变量
count += 1
return count
return counter
c = make_counter()
print(c()) # 1
print(c()) # 2
print(c()) # 3
建议:尽量避免使用全局变量和
global语句。全局状态难以追踪和测试,应通过参数传递数据。
五、lambda 匿名函数
5.1 基本语法
# 语法:lambda 参数列表: 表达式
square = lambda x: x ** 2
print(square(5)) # 25
add = lambda x, y: x + y
print(add(3, 4)) # 7
# 等价的普通函数
def square(x):
return x ** 2
5.2 lambda 的实际应用场景
lambda 最常用于需要一个小函数作为参数的场合:
# 排序的 key 参数
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 92},
{"name": "Charlie", "score": 78},
]
# 按成绩从高到低排序
sorted_students = sorted(students, key=lambda s: s["score"], reverse=True)
for s in sorted_students:
print(f"{s['name']}: {s['score']}")
# Bob: 92
# Alice: 85
# Charlie: 78
# 按字符串长度排序
words = ["banana", "apple", "cherry", "fig", "date"]
sorted_words = sorted(words, key=lambda w: len(w))
print(sorted_words) # ['fig', 'date', 'apple', 'banana', 'cherry']
# 多条件排序(先按成绩降序,再按名字升序)
sorted_students2 = sorted(
students,
key=lambda s: (-s["score"], s["name"])
)
六、高阶函数
高阶函数(Higher-order Function):接受函数作为参数,或返回函数的函数。
6.1 map():对序列每个元素应用函数
numbers = [1, 2, 3, 4, 5]
# 传统方式
squares = []
for n in numbers:
squares.append(n ** 2)
# 使用 map
squares = list(map(lambda x: x**2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# 字符串转整数
str_numbers = ["1", "2", "3", "4", "5"]
int_numbers = list(map(int, str_numbers))
print(int_numbers) # [1, 2, 3, 4, 5]
# 现代 Python 更推荐列表推导式(更 Pythonic)
squares = [x**2 for x in numbers]
6.2 filter():过滤序列
numbers = [1, -2, 3, -4, 5, -6, 7]
# 筛选正数
positives = list(filter(lambda x: x > 0, numbers))
print(positives) # [1, 3, 5, 7]
# 等价的列表推导式
positives = [x for x in numbers if x > 0]
6.3 sorted() 与 key 参数
# sorted() 不修改原列表,返回新列表
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_nums = sorted(numbers)
print(sorted_nums) # [1, 1, 2, 3, 4, 5, 6, 9]
print(numbers) # [3, 1, 4, 1, 5, 9, 2, 6](原列表不变)
# 降序
sorted_desc = sorted(numbers, reverse=True)
# 自定义排序规则
words = ["Banana", "apple", "Cherry", "date"]
# 忽略大小写排序
sorted_ci = sorted(words, key=str.lower)
print(sorted_ci) # ['apple', 'Banana', 'Cherry', 'date']
6.4 functools.reduce()
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# 累积求和
total = reduce(lambda x, y: x + y, numbers)
print(total) # 15
# 累积求积
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# 注意:大多数情况下,sum()/max()/min() 等内置函数比 reduce 更清晰
total = sum(numbers) # 更推荐
七、递归函数
7.1 什么是递归
递归(Recursion):函数调用自身。每个递归函数都需要:
- 基本情况(Base Case):不再递归,直接返回结果的条件
- 递归情况(Recursive Case):问题规模缩小,调用自身
# 经典例子:计算阶乘
def factorial(n):
"""计算 n 的阶乘(n!)。"""
# 基本情况
if n <= 1:
return 1
# 递归情况:n! = n × (n-1)!
return n * factorial(n - 1)
print(factorial(5)) # 120(5! = 5×4×3×2×1)
print(factorial(10)) # 3628800
递归执行过程(以 factorial(4) 为例):
factorial(4)
= 4 * factorial(3)
= 4 * (3 * factorial(2))
= 4 * (3 * (2 * factorial(1)))
= 4 * (3 * (2 * 1))
= 4 * (3 * 2)
= 4 * 6
= 24
7.2 递归的陷阱
# 错误:没有基本情况,会无限递归
def infinite_recursion(n):
return n + infinite_recursion(n - 1)
# RecursionError: maximum recursion depth exceeded
# Python 默认递归深度限制为 1000
import sys
print(sys.getrecursionlimit()) # 1000
# 可以修改,但不推荐
sys.setrecursionlimit(10000)
7.3 递归 vs 迭代
# 斐波那契数列:递归版(简洁但效率低)
def fib_recursive(n):
if n <= 1:
return n
return fib_recursive(n - 1) + fib_recursive(n - 2)
# 有大量重复计算!fib(40) 需要几秒
# 迭代版(高效)
def fib_iterative(n):
if n <= 1:
return n
a, b = 0, 1
for _ in range(n - 1):
a, b = b, a + b
return b
# 使用缓存优化递归(memoization)
from functools import lru_cache
@lru_cache(maxsize=None)
def fib_cached(n):
if n <= 1:
return n
return fib_cached(n - 1) + fib_cached(n - 2)
print(fib_cached(100)) # 极快!
八、函数注解(类型提示)
Python 3.5+ 支持类型注解,帮助 IDE 提示和静态检查工具:
def greet(name: str, times: int = 1) -> str:
"""重复打招呼。
Args:
name: 人名
times: 重复次数,默认 1
Returns:
格式化的问候语字符串
"""
return f"你好,{name}!" * times
# 类型注解不会在运行时强制检查,只是提示
result: str = greet("Alice", 3)
print(result)
# 复杂类型注解
from typing import List, Dict, Optional, Tuple, Union
def process_users(
users: List[Dict[str, str]],
admin: Optional[str] = None
) -> Tuple[int, List[str]]:
names = [user["name"] for user in users]
return len(users), names
九、综合实战:命令行计算器
"""
命令行计算器
综合运用:函数定义、参数、返回值、错误处理
"""
def add(a: float, b: float) -> float:
"""加法"""
return a + b
def subtract(a: float, b: float) -> float:
"""减法"""
return a - b
def multiply(a: float, b: float) -> float:
"""乘法"""
return a * b
def divide(a: float, b: float) -> float:
"""除法,除数为零时抛出 ValueError"""
if b == 0:
raise ValueError("除数不能为零")
return a / b
def power(a: float, b: float) -> float:
"""幂运算"""
return a ** b
# 运算符映射
OPERATIONS = {
"+": add,
"-": subtract,
"*": multiply,
"/": divide,
"**": power,
}
def parse_expression(expr: str):
"""解析数学表达式,返回 (操作数1, 运算符, 操作数2)"""
# 尝试各种运算符
for op in ["**", "+", "-", "*", "/"]:
if op in expr:
parts = expr.split(op, 1)
if len(parts) == 2:
try:
a = float(parts[0].strip())
b = float(parts[1].strip())
return a, op, b
except ValueError:
continue
raise ValueError(f"无法解析表达式:{expr}")
def calculate(expr: str) -> str:
"""计算表达式并返回结果字符串"""
a, op, b = parse_expression(expr)
operation_func = OPERATIONS[op]
result = operation_func(a, b)
# 如果结果是整数,去掉小数点
if result == int(result):
return f"{int(result)}"
return f"{result:.4f}"
def run_calculator():
"""运行计算器主循环"""
print("=" * 40)
print("Python 简易计算器")
print("支持运算:+ - * / **(幂)")
print("输入 'quit' 或 'exit' 退出")
print("示例:3 + 4 | 2 ** 10 | 10 / 3")
print("=" * 40)
while True:
try:
expr = input("\n>>> ").strip()
if not expr:
continue
if expr.lower() in ("quit", "exit", "q"):
print("再见!")
break
result = calculate(expr)
print(f"= {result}")
except ValueError as e:
print(f"❌ 输入错误:{e}")
except KeyboardInterrupt:
print("\n再见!")
break
# 运行计算器
run_calculator()
十、总结
本文全面讲解了 Python 函数的核心知识:
| 主题 | 关键点 |
|---|---|
| 函数定义 | def、函数名规范、Docstring |
| 位置参数 | 按顺序传入,与形参位置对应 |
| 关键字参数 | 按名称传入,顺序无关 |
| 默认参数 | 不传时使用默认值,不能用可变对象 |
*args |
收集多余位置参数为元组 |
**kwargs |
收集多余关键字参数为字典 |
| 返回值 | return 可多值返回(实为元组) |
| 作用域 LEGB | Local → Enclosing → Global → Built-in |
| lambda | 匿名小函数,适合 sorted(key=) 等场景 |
| 高阶函数 | map/filter/sorted,现代代码更推荐推导式 |
| 递归 | 必须有基本情况,避免深度过大 |
| 类型注解 | 文档化意图,配合 mypy 做静态检查 |
函数是 Python 编程的基石。下一篇,我们进入数据结构的世界——列表、元组、字典、集合,Python 最常用的四大容器,理解它们将让你处理复杂数据游刃有余。
上一篇:【第 005 篇】流程控制——条件判断与循环让程序活起来
下一篇:【第 007 篇】Python内置数据结构:列表、元组、字典、集合全解析
参考资料
- Python 官方文档 - 定义函数
- Python 官方文档 - 函数注解
- PEP 3107 — Function Annotations
- PEP 484 — Type Hints
- Python 官方文档 - functools
- Python 官方文档 - sorted()
- Real Python - Python Functions
更多推荐
所有评论(0)