

大家好,我是正在实战各种AI项目的程序员晚枫。
一个真实的故事
2024年,有个团队找我做代码审查。他们的项目有5000行代码,其中有一段"发送邮件"的逻辑,在10个不同的地方重复出现。
每次修改邮件格式,都要改10个地方。
结果呢?有一次改漏了1个地方,导致客户收到的邮件格式不一致,差点丢单。
我给他们封装了一个函数:
1 2 3 4 5 6 7
| def send_notification_email(to, subject, content, template="default"): """统一的邮件发送函数""" pass
|
修改后,5000行代码减少到3000行,维护成本降低80%。
这就是函数的力量——把重复的代码打包成工具,一行调用搞定。
上篇我们学了循环,能批量处理数据了。这篇来学函数——代码复用的核心武器。
学完这篇,你就能把重复的代码打包成工具,代码量减少,维护成本大幅下降。
什么是函数?
想象你在公司工作,每天都要寄快递:
- 填单子 → 打电话 → 等快递员 → 包装 → 交给他
如果每天都要完整走一遍,累不累?
现在公司引进了快递柜:你只需把东西放进去,一键下单,快递柜自动帮你搞定后续所有步骤。
**函数,就是代码里的"快递柜"**——你把数据放进去(传入参数),它自动执行打包好的流程,返回你想要的结果。
函数的好处
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| print("发送邮件给张三")
print("发送邮件给李四")
print("发送邮件给王五")
def send_email(to, content): pass
send_email("张三", "你好") send_email("李四", "你好") send_email("王五", "你好")
|
函数的三大好处:
- 复用性:写一次,到处用
- 可维护性:改一处,处处生效
- 可读性:名字即功能,代码更清晰
定义第一个函数
最简单的函数
1 2 3
| def say_hello(): """打印一句问候语""" print("你好,欢迎回来!")
|
逐行解释:
1 2 3 4 5 6
| def → 告诉Python,我要定义一个函数 say_hello → 函数的名字(你起的,要有意义) () → 空括号:不需要传入任何数据 : → 冒号:接下来是函数体 """...""" → 文档字符串(docstring):说明函数的作用 缩进的代码 → 函数要做的事
|
调用函数
定义完函数,不会自动执行——你需要"叫它来做事":
1 2 3 4 5 6 7 8
| def say_hello(): """打印一句问候语""" print("你好,欢迎回来!")
say_hello() say_hello() say_hello()
|
💡 类比:定义函数就像写一份操作手册,放在书架上;调用函数就像临时拿出来用一次。
函数的执行流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def greet(): print("第一步:打开门") print("第二步:说你好") print("第三步:关门")
print("程序开始") greet() print("程序结束")
|
带参数的函数:传入数据
给函数传入信息,让它根据不同输入产生不同结果。
单个参数
1 2 3 4 5 6 7
| def greet(name): """向指定的人打招呼""" print(f"你好,{name}!今天过得怎么样?")
greet("张三") greet("李四") greet("王五")
|
💡 参数(parameter):定义函数时的占位符,叫 name
实参(argument):调用函数时传入的具体值,叫 "张三"、"李四"
多个参数
1 2 3 4 5 6 7 8 9
| def introduce(name, age, city): """自我介绍""" print(f"我叫{name},今年{age}岁,来自{city}")
introduce("张三", 25, "北京")
introduce("李四", 30, "上海")
|
参数的顺序
1 2 3 4 5 6 7 8 9 10 11 12
| def create_profile(name, age, job): """创建用户档案""" return f"{name},{age}岁,职业:{job}"
profile1 = create_profile("张三", 25, "程序员")
profile2 = create_profile(age=30, name="李四", job="设计师")
profile3 = create_profile("王五", job="产品经理", age=28)
|
带返回值的函数:返回结果
有时候你不需要函数"打印"结果,而是需要它"返回"一个值给你。
return的作用
1 2 3 4 5 6 7 8 9 10 11
| def add(a, b): """计算两个数的和""" result = a + b return result
total = add(3, 5) print(f"3 + 5 = {total}")
print(f"10 + 20 = {add(10, 20)}")
|
运行结果:
💡 return 的作用:把结果交给调用者,之后还能用这个结果做其他事。
return vs print
1 2 3 4 5 6 7 8 9 10 11 12 13
| def add_print(a, b): print(a + b)
result = add_print(3, 5) print(result)
def add_return(a, b): return a + b
result = add_return(3, 5) print(result * 2)
|
提前返回
1 2 3 4 5 6 7 8
| def divide(a, b): """除法运算(带检查)""" if b == 0: return "错误:除数不能为0" return a / b
print(divide(10, 2)) print(divide(10, 0))
|
函数没有return
1 2 3 4 5
| def no_return(): print("这个函数没有return")
result = no_return() print(result)
|
返回多个值
Python函数可以一次性返回多个值:
返回元组
1 2 3 4 5 6 7 8 9 10 11 12 13
| def get_stats(scores): """返回最高分、最低分和平均分""" highest = max(scores) lowest = min(scores) average = sum(scores) / len(scores) return highest, lowest, average
top, bottom, avg = get_stats([85, 92, 78, 90, 88])
print(f"最高分:{top}") print(f"最低分:{bottom}") print(f"平均分:{avg:.1f}")
|
运行结果:
接收多个返回值的不同方式
1 2 3 4 5 6 7 8 9 10 11 12 13
| def get_user_info(): return "张三", 25, "北京"
name, age, city = get_user_info()
info = get_user_info() print(info)
name, *_ = get_user_info() print(name)
|
三种参数类型
① 位置参数:按顺序来
1 2 3 4 5
| def introduce(name, city, job): print(f"我是{name},在{city}做{job}工作")
introduce("张三", "北京", "程序员")
|
② 关键字参数:指定名字来传
1 2 3 4 5
| introduce(city="上海", name="李四", job="设计师")
introduce("王五", job="产品经理", city="广州")
|
③ 默认参数:给个默认值
1 2 3 4 5 6 7
| def greet(name, greeting="你好"): """greeting有默认值,不传就用默认值""" print(f"{greeting},{name}!")
greet("张三") greet("李四", "早上好") greet(name="王五", greeting="晚上好")
|
⚠️ 重要规则:默认参数要放在最后,不能放在必选参数前面!
1 2 3 4 5 6 7
| def wrong(greeting="你好", name): pass
def right(name, greeting="你好"): pass
|
默认参数的陷阱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def add_item(item, items=[]): items.append(item) return items
print(add_item("a")) print(add_item("b"))
def add_item(item, items=None): if items is None: items = [] items.append(item) return items
print(add_item("a")) print(add_item("b"))
|
可变参数:*args 和 **kwargs
*args:接收任意数量的位置参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def add_all(*numbers): """计算任意数量的数字之和""" total = 0 for num in numbers: total += num return total
print(add_all(1, 2, 3)) print(add_all(1, 2, 3, 4, 5)) print(add_all())
def show_args(*args): print(type(args)) print(args)
show_args(1, 2, 3, "a", "b")
|
**kwargs:接收任意数量的关键字参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def create_user(**info): """创建用户,接收任意关键字参数""" for key, value in info.items(): print(f"{key}: {value}")
create_user(name="张三", age=25, city="北京")
def show_kwargs(**kwargs): print(type(kwargs)) print(kwargs)
show_kwargs(a=1, b=2, c=3)
|
组合使用
1 2 3 4 5 6 7 8 9 10 11 12
| def func(required, default="默认值", *args, **kwargs): """完整的参数示例""" print(f"必选参数: {required}") print(f"默认参数: {default}") print(f"可变位置参数: {args}") print(f"可变关键字参数: {kwargs}")
func("必选", "默认", 1, 2, 3, name="张三", age=25)
|
变量作用域:哪里能看见哪里
局部变量 vs 全局变量
1 2 3 4 5 6 7 8 9
| name = "张三"
def introduce(): city = "北京" print(f"{name}住在{city}")
introduce() print(name)
|
作用域的查找顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| x = "全局"
def outer(): x = "外部函数" def inner(): x = "内部函数" print(x) inner() print(x)
outer() print(x)
|
在函数里修改全局变量
1 2 3 4 5 6 7 8 9 10 11
| count = 0
def click(): global count count += 1 print(f"点击了 {count} 次")
click() click() click() print(f"总点击:{count}")
|
💡 建议:尽量少用 global,多用参数和返回值——让函数只依赖传入的数据,不依赖外部变量,这样的函数更干净、更容易测试。
nonlocal:修改外部函数的变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def outer(): count = 0 def inner(): nonlocal count count += 1 return count return inner
counter = outer() print(counter()) print(counter()) print(counter())
|
函数文档:docstring
基本写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def add(a, b): """ 计算两个数的和 参数: a: 第一个数 b: 第二个数 返回: 两数之和 """ return a + b
print(add.__doc__) help(add)
|
Google风格的docstring
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def calculate_average(numbers): """计算数字列表的平均值 Args: numbers: 数字列表 Returns: float: 平均值 Raises: ValueError: 如果列表为空 Examples: >>> calculate_average([1, 2, 3]) 2.0 """ if not numbers: raise ValueError("列表不能为空") return sum(numbers) / len(numbers)
|
匿名函数:lambda
基本语法
1 2 3 4 5 6 7 8 9
| def add(a, b): return a + b
add_lambda = lambda a, b: a + b
print(add(1, 2)) print(add_lambda(1, 2))
|
lambda的应用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| students = [ {"name": "张三", "score": 85}, {"name": "李四", "score": 92}, {"name": "王五", "score": 78} ]
sorted_students = sorted(students, key=lambda x: x["score"], reverse=True) print(sorted_students)
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = list(filter(lambda x: x % 2 == 0, numbers)) print(evens)
squares = list(map(lambda x: x ** 2, numbers)) print(squares)
|
lambda的限制
1 2 3 4 5 6 7 8 9
|
def absolute(x): if x > 0: return x else: return -x
|
高阶函数:函数作为参数
函数是对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def greet(): return "你好"
say_hi = greet print(say_hi())
funcs = [greet, say_hi] for f in funcs: print(f())
def apply(func, value): return func(value)
print(apply(str.upper, "hello"))
|
高阶函数示例
1 2 3 4 5 6 7 8 9 10 11 12 13
| def calculate(a, b, operation): """根据传入的运算函数计算结果""" return operation(a, b)
def add(x, y): return x + y
def multiply(x, y): return x * y
print(calculate(10, 5, add)) print(calculate(10, 5, multiply)) print(calculate(10, 5, lambda x, y: x - y))
|
避坑指南
❌ 坑1:忘记return
1 2 3 4 5 6 7 8 9 10 11 12
| def add(a, b): result = a + b
print(add(1, 2))
def add(a, b): return a + b
print(add(1, 2))
|
❌ 坑2:修改可变参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def add_item(item, items): items.append(item) return items
my_list = [1, 2, 3] add_item(4, my_list) print(my_list)
def add_item_safe(item, items): new_items = items.copy() new_items.append(item) return new_items
|
❌ 坑3:默认参数用可变对象
1 2 3 4 5 6 7 8 9 10 11
| def add_item(item, items=[]): items.append(item) return items
def add_item(item, items=None): if items is None: items = [] items.append(item) return items
|
❌ 坑4:参数顺序错误
1 2 3 4 5 6 7 8 9
| def create_profile(name, age, city): pass
create_profile("张三", age=25, city="北京") create_profile(name="张三", age=25, city="北京")
|
实战练习:写一个工具函数库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
| """ 常用工具函数库 作者:程序员晚枫 """
def truncate(text, max_len=50, suffix="..."): """截断过长的文本 Args: text: 原文本 max_len: 最大长度 suffix: 截断后显示的后缀 Returns: 截断后的文本 """ if len(text) <= max_len: return text return text[:max_len] + suffix
def mask_email(email): """隐藏邮箱中间部分 Example: mask_email("zhangsan@example.com") # z***n@example.com """ if "@" not in email: return email name, domain = email.split("@") if len(name) <= 2: return name[0] + "***" + "@" + domain return name[0] + "***" + name[-1] + "@" + domain
def format_price(price, currency="¥"): """格式化价格 Args: price: 价格 currency: 货币符号 Returns: 格式化后的价格字符串 """ return f"{currency}{price:.2f}"
def calculate_discount(original_price, discount_rate): """计算折扣价 Args: original_price: 原价 discount_rate: 折扣率(0.8表示8折) Returns: 折扣价(保留两位小数) """ return round(original_price * discount_rate, 2)
def is_valid_email(email): """简单验证邮箱格式""" return "@" in email and "." in email.split("@")[-1]
def is_valid_phone(phone): """简单验证手机号格式(中国大陆)""" if not isinstance(phone, str): phone = str(phone) return len(phone) == 11 and phone.isdigit() and phone.startswith("1")
def grade_score(score): """根据分数返回等级 Args: score: 分数(0-100) Returns: 等级字符串 """ if not 0 <= score <= 100: return "无效分数" grades = [ (90, "A(优秀)"), (80, "B(良好)"), (70, "C(中等)"), (60, "D(及格)"), (0, "F(不及格)") ] for threshold, grade in grades: if score >= threshold: return grade
def get_stats(numbers): """获取数字列表的统计信息 Returns: dict: 包含最大值、最小值、平均值、总和 """ if not numbers: return None return { "max": max(numbers), "min": min(numbers), "avg": sum(numbers) / len(numbers), "sum": sum(numbers), "count": len(numbers) }
if __name__ == "__main__": print("=" * 50) print("工具函数库演示".center(44)) print("=" * 50) print("\n【字符串工具】") long_text = "这是一段很长的文本内容,用于测试截断功能是否正常工作" print(f"截断测试:{truncate(long_text, 20)}") print(f"邮箱隐藏:{mask_email('zhangsan@example.com')}") print("\n【数学工具】") print(f"价格格式化:{format_price(99.9)}") print(f"折扣计算:原价199,8折后 {format_price(calculate_discount(199, 0.8))}") print("\n【验证工具】") emails = ["user@example.com", "invalid", "test@163"] for email in emails: print(f"邮箱 {email}: {'✅' if is_valid_email(email) else '❌'}") print("\n【数据处理】") scores = [85, 92, 78, 90, 88] stats = get_stats(scores) print(f"统计信息:最高{stats['max']},最低{stats['min']},平均{stats['avg']:.1f}")
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ================================================== 工具函数库演示 ==================================================
【字符串工具】 截断测试:这是一段很长的文本内容,用于测试... 邮箱隐藏:z***n@example.com
【数学工具】 价格格式化:¥99.90 折扣计算:原价199,8折后 ¥159.20
【验证工具】 邮箱 user@example.com: ✅ 邮箱 invalid: ❌ 邮箱 test@163: ✅
【数据处理】 统计信息:最高92,最低78,平均86.6
|
性能对比:函数调用的开销
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import timeit
def direct(): result = 0 for i in range(1000): result += i return result
def add(a, b): return a + b
def with_function(): result = 0 for i in range(1000): result = add(result, i) return result
print(f"直接计算: {timeit.timeit(direct, number=10000):.4f}秒") print(f"函数调用: {timeit.timeit(with_function, number=10000):.4f}秒")
|
结论: 函数调用有一定开销,但可读性和维护性的收益远大于这点性能损失。不要为了微小的性能提升而牺牲代码质量。
📚 推荐:Python 零基础实战营
系统学习Python,推荐这个免费入门课程 👇
| 特点 | 说明 |
|---|
| 🎯 专为0基础设计 | 门槛低,上手快 |
| 📹 配套视频讲解 | 配合文章学习效果更好 |
| 💬 专属答疑群 | 遇到问题有人带 |
| 🎁 实体书赠送 | 优秀学员送《Python编程从入门到实践》 |
👉 点击免费领取 Python 零基础实战营
本讲小结
| 概念 | 说明 |
|---|
def 函数名(): | 定义函数 |
| 参数 | 函数接收的输入 |
return 值 | 函数返回的结果 |
| 位置参数 | 按顺序传入 |
| 关键字参数 | 指定名字传入 |
| 默认参数 | 有默认值的参数(放在最后) |
*args | 接收任意数量的位置参数 |
**kwargs | 接收任意数量的关键字参数 |
| 局部变量 | 只在函数内部有效 |
| 全局变量 | 整个文件都能访问 |
global | 声明使用全局变量 |
| docstring | 函数的文档说明 |
| lambda | 匿名函数 |
下节预告
学会了函数的定义和使用,下一篇来学函数参数进阶——*args和**kwargs的更多用法,以及装饰器的入门。
你将学会:
👉 继续阅读:Python函数参数
课程导航
上一篇: Python循环-for和while完全指南
下一篇: Python函数参数*args和**kwargs
相关阅读
PS:函数是代码复用的基础。看到重复的代码,就想:"能不能封装成函数?"这个习惯养成了,你写代码的效率会翻倍。记住:好的函数应该像黑盒——输入进去,结果出来,不需要知道内部细节。
2026-04-23 更新 by 程序员晚枫
🎓 AI 编程实战课程
想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!