Python 函数详解
目录
- 函数的基本概念
- 函数的定义与调用
- 函数参数
- 返回值
- 变量作用域
- 匿名函数
- 递归函数
- 高阶函数
- 练习题
1. 函数的基本概念
什么是函数?
函数是一段可重复使用的代码块,用于执行特定任务。它接受输入(参数),进行处理,并返回结果。
就类似于一个加工厂,你给它一些原材料,它帮你进行处理之后返回成品。
生活比喻:
- 计算器(函数):输入两个数和运算符(原料),内部计算(步骤),输出结果(成品)
- 洗衣机(函数):输入脏衣服和洗衣液(原料),内部清洗甩干(步骤),输出干净衣服(成品)
为什么使用函数?---解决重复劳动的问题
没有函数的问题:
如果每次计算"两个数的和"都要写 a + b,每次打印"欢迎语"都要写 print("欢迎使用!"),代码会变得冗长、重复,改一处要改很多地方。
函数的好处:
- 复用性:一段代码写一次,多次调用,不用重复写
- 简洁性:用一个名字代替一堆代码,逻辑更清晰
- 可维护性:改功能只需要改函数内部,不用到处改
2. 基础语法、分类、参数与返回值
函数的基本语法
函数是封装可复用逻辑的"工具",用 def 关键字定义,核心是"定义逻辑 → 调用执行"。
1. 函数的定义:"造工具"的步骤
语法格式:
def 函数名(参数列表):
"""函数文档字符串(可选,说明功能)"""
函数体代码(缩进块)
return 返回值(可选)
def:标记"开始定义函数"的关键字函数名:自定义名称(见名知意,如calculate_sum表示"计算和"),遵循变量命名规则参数列表:函数的"输入原料",可包含 0 个或多个参数,多参数用逗号分隔函数体:缩进的代码块,是函数具体执行的逻辑return:"输出结果"的关键字,可返回任意数据;若无return,函数默认返回None
2. 函数的调用:"用工具"的过程
定义后,通过 函数名(实际参数) 触发执行。
示例 1:无参数、无返回值
def say_hello():
"""打印欢迎语"""
print("你好,欢迎学习Python函数!")
say_hello() # 调用:输出"你好,欢迎学习Python函数!"
say_hello() # 再次调用,重复执行逻辑
示例 2:有参数、有返回值
def calculate_sum(a, b):
"""计算两个数的和并返回结果"""
result = a + b
return result
sum1 = calculate_sum(3, 5)
print("3 + 5 =", sum1) # 输出:3 + 5 = 8
sum2 = calculate_sum(10, 20)
print("10 + 20 =", sum2) # 输出:10 + 20 = 30
3. 函数文档字符串(可选但建议写)
用三引号 """ 包裹的字符串,放在函数体开头,用于说明功能、参数、返回值(方便他人理解)。
示例:
def calculate_product(x, y):
"""
计算两个数的乘积。
参数:
x (int/float):第一个乘数。
y (int/float):第二个乘数。
返回:
int/float:两个数的乘积。
"""
return x * y
# 查看文档(交互式环境用 help(calculate_product))
print(calculate_product.__doc__)
函数的分类
根据参数、返回值、功能,函数可分为以下 4 类:
1. 无参数、无返回值的函数
- 特点:无需输入"原料",也不输出"成品",仅执行逻辑(如打印、修改全局变量)
- 示例:
def show_menu():
print("===== 菜单 =====")
print("1. 查看个人信息")
print("2. 修改密码")
print("3. 退出系统")
2. 无参数、有返回值的函数
- 特点:无需输入,但会输出结果
- 示例:
import random
def get_random_num():
"""返回1-100的随机整数"""
return random.randint(1, 100)
num = get_random_num()
print("随机数:", num) # 输出:随机数:xxx(1-100之间)
3. 有参数、无返回值的函数
- 特点:需要输入"原料",但不输出"成品"(常用于直接执行动作)
- 示例:
def print_info(name, age):
"""打印个人信息"""
print(f"姓名:{name},年龄:{age}岁")
print_info("张三", 20) # 输入参数,执行打印,无返回值
4. 有参数、有返回值的函数
def calculate_discount(price, rate):
"""计算打折后价格"""
final = price * rate
return final
original = 100
discounted = calculate_discount(original, 0.8)
print(f"原价{original}元,折后{discounted}元") # 输出:原价100元,折后80.0元
3. 函数的参数
1. 位置参数(必选参数)
- 特点:调用时,实际参数的顺序、数量必须与定义时完全匹配
- 示例:
def describe_person(name, age, city):
print(f"姓名:{name},年龄:{age}岁,城市:{city}")
describe_person("李四", 25, "北京") # 正确:按位置一一对应
# describe_person("王五", 30) # 错误:缺少 city 参数
2. 默认值参数
- 特点:定义时给参数设默认值;调用时,若不传递该参数则用默认值
- 注意:默认参数必须放在位置参数之后
- 示例:
def describe_person(name, age, city="上海"): # city 有默认值
print(f"姓名:{name},年龄:{age}岁,城市:{city}")
describe_person("李四", 25) # 用默认值 → 输出:姓名:李四,年龄:25岁,城市:上海
describe_person("王五", 30, "广州") # 覆盖默认值 → 输出:姓名:王五,年龄:30岁,城市:广州
3. 关键字参数
- 特点:调用时通过
参数名=值传递,无需遵循位置顺序,但参数名必须与定义一致 - 示例:
def describe_person(name, age, city):
print(f"姓名:{name},年龄:{age}岁,城市:{city}")
# 关键字参数调用,顺序可任意
describe_person(age=25, name="李四", city="北京") # 正确
4. 可变长参数
用于处理不确定数量的参数:
*args:接收任意数量的位置参数,打包成元组**kwargs:接收任意数量的关键字参数,打包成字典
*args 示例:
def calculate_total(*numbers):
"""计算任意多个数的和"""
total = 0
for num in numbers:
total += num
return total
print(calculate_total(1, 2)) # 输出:3
print(calculate_total(1, 2, 3, 4, 5)) # 输出:15
**kwargs 示例:
def show_info(**info):
"""打印任意多的关键字参数"""
for key, value in info.items():
print(f"{key}:{value}")
show_info(name="张三", age=22, major="计算机")
# 输出:
# name:张三
# age:22
# major:计算机
同时使用 args 和 *kwargs:
def func(*args, **kwargs):
print("位置参数打包:", args) # 元组形式
print("关键字参数打包:", kwargs) # 字典形式
func(1, 2, name="小明", score=90)
# 输出:
# 位置参数打包: (1, 2)
# 关键字参数打包: {'name': '小明', 'score': 90}
参数顺序
注意参数的顺序:# 参数顺序:位置参数 -> 可变参数 -> 默认值参数 -> 关键字参数
def fn(name, *args, age=19, **kwargs):
print("位置参数:", name)
print("可变参数:", args)
print("默认值参数:", age)
print("关键字参数:", kwargs)
# fn("tom")
# fn("jerry", "java", gender="男")
fn("jerry", "java", 29, age=30, gender="男")
4. 函数的返回值
return 是函数的"成品出口",用于将内部结果传递到外部。
1. 返回单个值
可返回任意数据类型(int、float、str、列表、字典等)。
示例:
def get_max(num1, num2):
"""返回两个数中的较大值"""
return num1 if num1 > num2 else num2
max_num = get_max(10, 20)
print("较大的数:", max_num) # 输出:较大的数:20
2. 返回多个值
Python 中 return 会将多个值打包成元组,调用时可自动解包为多个变量。
示例:
def calculate_stats(numbers):
"""返回列表的总和与平均值"""
total = sum(numbers)
average = total / len(numbers)
return total, average # 多返回值 → 元组打包
nums = [1, 2, 3, 4, 5]
total, avg = calculate_stats(nums) # 元组解包
print(f"总和:{total},平均值:{avg}") # 输出:总和:15,平均值:3.0
3. return 的"终止"作用
函数执行到 return 时会立即终止,后续代码不再执行。
示例:
def check_age(age):
if age < 0:
return "年龄不能为负数!"
print("这句代码在 age<0 时不会执行") # 被 return 终止
print(check_age(-5)) # 输出:年龄不能为负数!
print(check_age(20)) # 输出:这句代码在 age<0 时不会执行 → None
4. 无 return 时的默认返回值
若函数无 return 或 return 后无值,默认返回 None(表示"无结果")。
示例:
def say_hi():
print("Hi!")
result = say_hi()
print(result) # 输出:Hi! → None(先打印 Hi!,再打印返回的 None)
5. 全局变量和局部变量
在 Python 中,变量的"作用范围"(即哪里能访问到这个变量)是区分全局变量和局部变量的核心。简单说:全局变量是"大家都能用"的变量,局部变量是"只有特定小范围能用"的变量
全局变量
定义:在函数外部定义的变量,就是全局变量。它的作用域(能被访问的范围)是整个程序(包括所有函数内部,只要没被局部变量覆盖)。
# 在函数外部定义全局变量
global_num = 100 # 全局变量:整个程序都能访问
def print_global():
# 函数内部可以直接访问全局变量
print("函数内访问全局变量:", global_num)
def use_global():
# 函数内部可以使用全局变量进行计算
result = global_num * 2
print("用全局变量计算的结果:", result)
# 调用函数
print_global() # 输出:函数内访问全局变量:100
use_global() # 输出:用全局变量计算的结果:200
# 函数外部也能直接访问
print("函数外访问全局变量:", global_num) # 输出:函数外访问全局变量:100
局部变量
定义:在函数内部定义的变量,就是局部变量。它的作用域仅限当前函数内部,函数外部或其他函数都无法访问。
def create_local():
# 在函数内部定义局部变量
local_str = "我是局部变量" # 局部变量:仅在create_local()内可用
print("函数内访问局部变量:", local_str)
create_local() # 输出:函数内访问局部变量:我是局部变量
# 尝试在函数外部访问局部变量 → 报错!
# print("函数外访问局部变量:", local_str) # 报错:NameError: name 'local_str' is not defined
函数的执行过程
当调用一个函数时,系统会在 栈内存 中为该函数单独分配一块临时内存区域。这块区域专门用来存储:
- 函数的参数
- 函数内部定义的局部变量
- 函数执行的中间状态
此时,局部变量就"存放在这块临时区域里",只能在函数内部被访问(相当于"临时房间里的物品,只有房间内的操作能触及")
函数执行完:"出栈",局部变量的"临时房间"被销毁
当函数执行完毕(比如遇到
return或函数体结束),栈会执行 "出栈"操作——回收并销毁该函数的临时内存区域。这意味着:存储局部变量的内存空间被系统释放,局部变量彻底"消失"了。
通俗理解:
- 调用函数 → 租一个临时仓库(栈中分配临时内存),把局部变量当"货物"存在仓库里
- 函数执行完 → 退租仓库(栈回收内存),仓库被销毁,里面的"货物"(局部变量)也跟着消失,外部自然找不到
特殊情况:同名变量与 global 关键字
全局变量与局部变量同名
num = 100 # 全局变量
def test_same_name():
num = 200 # 局部变量(和全局变量同名)
print("函数内的num(局部):", num) # 优先用局部变量 → 200
test_same_name()
print("函数外的num(全局):", num) # 全局变量没变 → 100
global 关键字
默认情况下,函数内只能"访问"全局变量,不能直接"修改"它(如果直接赋值,会被当成定义局部变量)。如果要修改全局变量,必须在函数内用 global 关键字声明。
count = 0 # 全局变量
def correct_modify():
global count # 声明:我要修改的是全局变量count
count = 1 # 现在才是真正修改全局变量
print("函数内的count(全局):", count) # 输出:1
correct_modify()
print("函数外的count(全局):", count) # 全局变量已被修改 → 1