函数基础
函数是Python中最基本的代码抽象单元。通过函数,我们可以将复杂的计算过程封装成可重用的代码块,让程序更加模块化、易于理解和维护。
为什么需要函数?
假设要计算圆的面积,公式是 S = πr²。如果不用函数,我们需要重复写计算代码:
Python
# 每次计算都要写这个公式
r1 = 12.34
s1 = 3.14 * r1 * r1
print(s1)
r2 = 9.08
s2 = 3.14 * r2 * r2
print(s2)
r3 = 73.1
s3 = 3.14 * r3 * r3
print(s3)
问题:
- 代码重复,写起来麻烦
- 如果要改公式(比如把π改成更精确的值),需要改很多地方
- 代码可读性差
用函数封装后:
Python
def area_of_circle(r):
return 3.14 * r * r
print(area_of_circle(12.34))
print(area_of_circle(9.08))
print(area_of_circle(73.1))
函数的优点
- 代码复用:一次定义,多次调用
- 易于维护:修改只需改一处
- 提高可读性:函数名表达业务含义
- 模块化:将大问题拆成小问题
抽象:程序设计的核心思想
┌─────────────────────────────────────────────────────────────────────┐ │ 抽象的力量 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 数学中的抽象: │ │ │ │ ∑(n=1 to 100) n² ─── 抽象记法,一眼看出是"求平方和" │ │ │ │ │ ▼ │ │ (1²+1) + (2²+1) + ... + (100²+1) ─── 具体计算过程 │ │ │ │ 程序中的抽象: │ │ │ │ area_of_circle(r) ─── 抽象,一眼看出是"计算圆面积" │ │ │ │ │ ▼ │ │ 3.14 * r * r ─── 具体实现细节 │ │ │ │ 关键: 抽象让你关心"做什么",不关心"怎么做" │ │ │ └─────────────────────────────────────────────────────────────────────┘
定义函数
使用def关键字定义函数,语法格式为:
┌─────────────────────────────────────────────────────────────────────┐ │ def 函数名(参数): │ │ 函数体 │ │ [return 返回值] │ └─────────────────────────────────────────────────────────────────────┘
Python
# 无参数函数
def say_hello():
print("Hello, World!")
# 带一个参数的函数
def greet(name):
print(f"你好,{name}!")
# 带多个参数的函数
def add(a, b):
return a + b
# 调用函数
say_hello() # Hello, World!
greet("张三") # 你好,张三!
result = add(1, 2) # result = 3
print(result)
返回值详解
函数可以使用return语句返回值。如果没有return语句或return后没有值,函数返回None。
┌─────────────────────────────────────────────────────────────────────┐ │ return 执行流程 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ def func(x): │ │ if x < 0: │ │ return None ◀── 提前返回 │ │ return x * 2 ◀── 正常返回 │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ 调用 func(-5) ──▶ x = -5 ──▶ x < 0 ──▶ return None │ │ │ │ 调用 func(10) ──▶ x = 10 ──▶ x >= 0 ──▶ return 20 │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 单返回值
def square(x):
return x ** 2
print(square(5)) # 25
# 多返回值 (实际返回的是元组)
def divide(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder
result = divide(17, 5)
print(result) # (3, 2)
print(result[0]) # 3
print(result[1]) # 2
# 简洁的元组解包
q, r = divide(17, 5)
print(f"商={q}, 余数={r}") # 商=3, 余数=2
# 没有 return 语句
def do_nothing():
pass
print(do_nothing()) # None
# return 后没有值
def maybe_return(x):
if x < 0:
return
return x * 2
print(maybe_return(-5)) # None
print(maybe_return(10)) # 20
函数调用的内存模型
┌─────────────────────────────────────────────────────────────────────┐ │ 函数调用内存模型 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 代码: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ x = 10 # 变量 x = 10 │ │ │ │ y = square(x) # 调用函数 │ │ │ │ # 1. 创建栈帧 │ │ │ │ # 2. 参数 x=10 传入 │ │ │ │ # 3. 计算 x**2 = 100 │ │ │ │ # 4. 返回 100 │ │ │ │ # 5. 栈帧销毁 │ │ │ │ y = 100 # 结果赋值给 y │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 栈帧示意图: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ 调用前: 调用中 (square函数): 调用后: │ │ │ │ │ │ │ │ ┌─────┐ ┌─────────────────┐ ┌─────┐ │ │ │ │ │ x=10│ │ square栈帧: │ │ x=10│ │ │ │ │ └─────┘ │ 参数: x=10 │ │ y=100│ │ │ │ │ │ 局部: 无 │ └─────┘ │ │ │ │ │ 返回: 100 │ │ │ │ │ └─────────────────┘ │ │ │ │ │ │ │ │ 主调者栈帧 被调者栈帧 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 关键点: │ │ • 每个函数调用都会创建一个新的栈帧 │ │ • 函数执行完毕,栈帧被销毁 │ │ • 局部变量在函数外部无法访问 │ │ │ └─────────────────────────────────────────────────────────────────────┘
默认参数
为参数指定默认值,调用时可以省略该参数。
Python
def greet(name, greeting="你好", punctuation="!"):
return f"{greeting},{name}{punctuation}"
# 使用所有参数
print(greet("张三", "早上好", "~")) # 早上好,张三~
# 省略默认参数
print(greet("李四")) # 你好,李四!
# 只提供部分默认参数
print(greet("王五", "晚上好")) # 晚上好,王五!
默认参数陷阱
不要使用可变对象作为默认参数!
# 错误示例 (可变对象作为默认参数)
def bad_func(items=[]): # 危险![] 是可变对象
items.append(1)
return items
print(bad_func()) # [1]
print(bad_func()) # [1, 1] -- 不是 [1]!
# 正确示例
def good_func(items=None):
if items is None:
items = []
items.append(1)
return items
print(good_func()) # [1]
print(good_func()) # [1] -- 正确!
┌─────────────────────────────────────────────────────────────────────┐ │ 默认参数陷阱原因分析 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ def bad_func(items=[]): # 默认参数在函数定义时创建 │ │ │ │ 调用bad_func()第一次: │ │ items = [] (使用默认的同一个列表对象) │ │ items.append(1) ──▶ items = [1] │ │ 函数结束,items引用被删除,但默认列表对象还在! │ │ │ │ 调用bad_func()第二次: │ │ items = [] (使用的是同一个默认列表对象,已经有[1]) │ │ items.append(1) ──▶ items = [1, 1] │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 内存中: │ │ │ │ ┌─────────────────┐ │ │ │ │ │ 默认列表对象: │ ← 第一次调用后被修改 │ │ │ │ │ [1] │ ← 第二次调用时直接使用 │ │ │ │ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 解决: 使用 None 作为默认值,在函数内部创建新列表 │ │ │ └─────────────────────────────────────────────────────────────────────┘
变量作用域
Python中有四种作用域,从内到外是:局部( Local ) → 闭包( Enclosing ) → 全局( Global ) → 内置( Built-in )
┌─────────────────────────────────────────────────────────────────────┐ │ LEGB 作用域规则 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Built-in: len, print, range 等内置函数/常量 │ │ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Global: 模块顶部定义的变量 │ │ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Enclosing: 嵌套函数的外层函数的变量 │ │ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Local: 函数内部定义的变量 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 查找顺序: L → E → G → B │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
x = 10 # 全局变量
z = 100 # 全局变量
def outer():
y = 20 # 闭包变量 (enclosing)
def inner():
x = 30 # 局部变量 (local)
print(f"局部变量 x = {x}") # 30
print(f"闭包变量 y = {y}") # 20
print(f"全局变量 z = {z}") # 100
inner()
# print(x) # 错误!x 是 inner 的局部变量
outer()
print(f"全局变量 x = {x}") # 10
Python
# global 语句: 在函数内修改全局变量
counter = 0
def increment():
global counter # 声明要使用全局变量
counter += 1
return counter
print(increment()) # 1
print(increment()) # 2
print(counter) # 2
# nonlocal 语句: 在嵌套函数中修改闭包变量
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
func = outer()
print(func()) # 1
print(func()) # 2
print(func()) # 3
慎用 global
过度使用global会使代码难以理解和维护。推荐的做法是:
- 尽量通过参数传递和返回值处理数据
- 使用类来封装相关的数据和函数
- global只用于保存全局配置等少数场景
pass语句
pass是空操作语句,不做任何事情,用作占位符。
Python
# 定义空函数 (还没想好函数体)
def todo_func():
pass # TODO: 实现这个函数
# 条件分支占位
if condition:
pass # 暂时什么都不做
else:
print("执行其他操作")
# 类定义占位
class EmptyClass:
pass
文档字符串 (Docstring)
在函数开头使用三引号添加函数说明,这些内容可以通过__doc__属性访问。
Python
def calculate_area(radius, pi=3.14159):
"""
计算圆的面积
参数:
radius: 圆的半径 (必须)
pi: 圆周率 (可选,默认 3.14159)
返回:
圆的面积 (float)
示例:
>>> calculate_area(5)
78.53975
>>> calculate_area(5, 3.14)
78.5
"""
return pi * radius ** 2
# 查看文档
print(calculate_area.__doc__)
# 使用 help 函数查看格式化后的文档
help(calculate_area)
┌─────────────────────────────────────────────────────────────────────┐ │ Docstring 风格指南 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 单行 Docstring: │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ def add(a, b): │ │ │ │ """Return the sum of a and b.""" │ │ │ │ return a + b │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ 多行 Docstring: │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ def func(arg1, arg2): │ │ │ │ """Summary line. │ │ │ │ │ │ │ │ Extended description of the function. │ │ │ │ │ │ │ │ Args: │ │ │ │ arg1: Description of arg1 │ │ │ │ arg2: Description of arg2 │ │ │ │ │ │ │ │ Returns: │ │ │ │ Description of return value │ │ │ │ """ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
函数是一等公民
在Python中,函数和其他值一样,可以:
- 赋值给变量
- 作为参数传递给其他函数
- 作为函数的返回值
Python
# 函数赋值给变量
def square(x):
return x ** 2
my_func = square # 不用括号,只是把函数赋值给变量
print(my_func(5)) # 25
# 函数作为参数
def apply_twice(func, x):
return func(func(x))
def double(x):
return x * 2
print(apply_twice(double, 5)) # 20 (5*2*2)
# 函数作为返回值
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
times_3 = make_multiplier(3)
times_5 = make_multiplier(5)
print(times_3(10)) # 30
print(times_5(10)) # 50
章节测验
问题1: 下面代码的输出是什么?
def func(x, y=5):
return x + y
print(func(3))
问题2: 以下哪个是Python函数的正确返回值?
问题3: 函数内部可以修改全局变量吗?
问题4: 下面哪个是使用可变对象作为默认参数的问题?
练习题
练习1: 温度转换函数
编写华氏温度转摄氏温度的函数,公式:C = (F - 32) * 5 / 9
输入: fahrenheit_to_celsius(100)
输出: 37.77777777777778
练习2: 回文判断
编写函数判断一个字符串是否是回文(正读和反读都一样)。
输入: is_palindrome("上海自来水来自海上")
输出: True
练习3: 计算阶乘
编写函数计算 n 的阶乘(使用递归或循环实现)。
输入: factorial(5)
输出: 120 (5! = 5*4*3*2*1)
🐍 Python 在线代码编辑器
📝 运行结果