Python函数基础:定义、参数、返回值与作用域入门
当我们刚学完 Python 的基础语法之后,往往已经可以写出一些简单的小脚本了:读取输入、做判断、写循环、处理列表和字典。可是只要代码一多,你很快就会发现一个问题:逻辑开始重复,代码开始变长,维护也开始变麻烦。
这时候,函数就该出场了。
函数的价值,不只是“把几行代码包起来”这么简单。更重要的是,它能帮我们把一段逻辑起一个明确的名字,让代码结构从“顺着往下写的脚本”变成“可以拆分、复用、组合的模块”。这一点,在后面学习模块、面向对象、Web 开发时都会不断体现出来。
本文就围绕 Python 中最基础也最重要的函数知识,系统讲清楚以下几个问题:
- 什么是函数,为什么要用函数;
- 如何定义函数;
- 参数有哪些常见写法;
- 返回值到底是怎么工作的;
- 局部变量与作用域该怎么理解;
- 编写函数时有哪些常见陷阱与建议。
一、为什么要学函数?
先看一个最直观的例子。假设我们需要多次计算矩形面积:
1 | length1 = 5 |
这段代码当然能运行,但问题也很明显:
- 逻辑重复;
- 如果以后计算方式要改,得改很多处;
- 代码含义不够集中,看起来像“过程”,不像“能力”。
如果改成函数:
1 | def calculate_area(length, width): |
一下就清晰了很多。
你可以把函数理解成:
给一段可复用的逻辑起名字,并且允许我们在需要的时候传入数据、得到结果。
函数最核心的好处主要有三点:
- 减少重复代码;
- 让程序结构更清晰;
- 更容易测试、维护和扩展。
很多初学者前期写代码,最大的问题不是语法不会,而是所有逻辑都堆在一起。函数就是从“会写脚本”走向“会组织代码”的第一步。
二、函数的定义与调用
2.1 最基本的函数写法
在 Python 中,函数通过 def 关键字定义:
1 | def greet(name): |
这段代码的意思是:
def表示定义函数;greet是函数名;name是参数;- 缩进部分是函数体。
调用时这样写:
1 | greet('Alice') |
输出:
1 | 你好,Alice |
这里要特别注意:
greet表示函数本身;greet('Alice')才表示调用函数。
2.2 函数名应该怎么起?
函数名建议遵循下面这些规则:
- 使用小写字母;
- 多个单词之间使用下划线分隔;
- 尽量让名字表达出行为,而不是模糊描述。
例如:
1 | def get_user_name(): |
比起 foo()、test1()、do_it() 这类名字,可读性会高很多。
三、参数:函数如何接收外部数据
函数之所以有用,很大程度上就在于它能处理不同输入。
3.1 位置参数
最常见的是位置参数。
1 | def add(a, b): |
调用时 3 会传给 a,5 会传给 b。
这种写法要求:参数顺序要对应。
1 | print(add(5, 3)) |
这时候虽然结果还是 8,但对于减法、除法等函数来说,顺序就会直接影响结果。
3.2 默认参数
有些参数在大多数情况下取值固定,就可以给它设置默认值。
1 | def greet(name, greeting='你好'): |
输出:
1 | 你好,小王 |
默认参数的意义在于:
- 常用场景可以少传一个参数;
- 接口更友好;
- 函数更容易复用。
不过有两个规则要记住:
- 默认参数要放在非默认参数后面;
- 默认值最好使用不可变对象。
例如:
1 | def register(username, role='user'): |
这是合法且常见的写法。
3.3 关键字参数
调用函数时,我们还可以通过“参数名=值”的方式传参。
1 | def introduce(name, age, city): |
关键字参数的好处是:
- 可读性更好;
- 不容易把顺序写错;
- 某些参数较多的函数更适合这种写法。
3.4 位置参数和关键字参数可以混用吗?
可以,但要遵守规则:
- 位置参数必须写在前面;
- 关键字参数写在后面。
例如:
1 | def create_user(name, age, city='上海'): |
这是合法的。
但如果把关键字参数写前面、位置参数写后面,就会报错。
四、返回值:函数执行完之后把结果交回来
很多初学者一开始会把“打印结果”和“返回结果”混在一起,这两者其实完全不是一回事。
4.1 print() 不等于返回值
先看这个函数:
1 | def add(a, b): |
调用:
1 | result = add(2, 3) |
输出会是:
1 | 5 |
为什么?
因为这个函数只是把结果打印出来了,但没有返回结果。所以变量 result 拿到的是 None。
4.2 正确返回结果要用 return
1 | def add(a, b): |
这时候 result 才真正拿到了 5。
你可以这样理解:
print()是“展示给人看”;return是“交给程序后续继续使用”。
在真实开发里,函数更重要的是 return,因为返回值才意味着函数可以继续参与后面的逻辑。
4.3 一个函数可以有多个 return
1 | def compare(a, b): |
函数会在遇到 return 时立刻结束执行。
4.4 不写 return 会怎样?
如果函数没有显式写 return,那么默认返回 None。
1 | def say_hello(name): |
输出最后会多出一个 None。
这不是错误,而是 Python 的默认行为。
4.5 返回多个值是怎么回事?
Python 常说“一个函数可以返回多个值”,其实本质上返回的是一个元组。
1 | def get_user_info(): |
输出:
1 | ('Alice', 20, 'Shanghai') |
也可以直接拆包:
1 | name, age, city = get_user_info() |
这种写法在 Python 中很常见,比如后面处理文件、坐标、配置项时都会用到。
五、作用域:函数里的变量和外面的变量是什么关系?
函数不仅仅涉及参数和返回值,还涉及一个非常重要的概念:变量作用域。
5.1 局部变量
定义在函数内部的变量,默认只在函数内部有效。
1 | def demo(): |
这里的 message 是局部变量,出了函数就不能直接访问。
5.2 全局变量
定义在函数外部的变量,通常称为全局变量。
1 | name = 'Python' |
这种情况下,函数内部可以读取外部变量。
5.3 为什么要强调作用域?
因为如果你对变量到底来自哪里不清楚,代码一复杂就很容易乱。
一个很常见的坏习惯是:
- 函数大量依赖外部变量;
- 参数传得很少;
- 看函数本身根本不知道它需要什么上下文。
更推荐的方式是:
- 优先通过参数传入需要的数据;
- 通过返回值交出结果;
- 少让函数偷偷依赖外部环境。
这样函数会更独立,也更容易测试。
5.4 修改全局变量为什么要小心?
如果你在函数内部给一个和全局变量同名的变量赋值,Python 会优先把它当成局部变量。
1 | count = 10 |
输出:
1 | 20 |
说明函数内部的 count 和外部的 count 其实不是同一个变量。
如果确实要在函数里修改全局变量,可以使用 global,但初学阶段不建议滥用。
1 | count = 10 |
虽然这样能改成功,但从代码设计角度看,函数频繁修改全局状态通常不是好习惯。
六、一个非常容易踩坑的问题:默认参数不要乱用可变对象
这是 Python 函数里非常经典的一个坑。
先看代码:
1 | def add_item(item, items=[]): |
很多人以为输出会是:
1 | ['a'] |
但实际输出是:
1 | ['a'] |
原因在于:默认参数只在函数定义时创建一次。items=[] 这个列表不会在每次调用时重新生成,而是被反复复用。
正确写法通常是:
1 | def add_item(item, items=None): |
这是 Python 里非常常见、也非常值得记住的写法。
七、函数文档与注释:让函数更容易被别人理解
当函数开始变多之后,仅靠名字还不一定足够。此时可以使用文档字符串来补充说明。
1 | def calculate_bmi(weight, height): |
文档字符串的价值主要在于:
- 告诉别人这个函数是干什么的;
- 帮助 IDE 和
help()输出说明; - 函数多了以后更容易维护。
如果函数稍复杂一点,还可以把参数和返回值说明写进去:
1 | def convert_score(score): |
虽然你现在写的小函数可能觉得“没必要”,但一旦进入多人协作或过几个月再回头看自己的代码,就会发现这类说明真的很重要。
八、综合示例:把零散逻辑整理成函数
下面我们做一个简单的小练习:把学生成绩处理逻辑拆成多个函数。
1 | def get_level(score): |
这个例子虽然简单,但已经体现出函数拆分的价值:
get_level()负责等级判断;is_pass()负责判断是否及格;print_report()负责组织输出。
如果以后规则变化,比如“85 分以上就算优秀”,你只需要改其中一个函数,而不是全局到处找判断逻辑。
这就是函数带来的结构化收益。
九、学习建议:初学函数时该重点抓什么?
9.1 先分清参数、返回值、打印输出
这是最基础也最容易混的点:
- 参数:传进去的数据;
- 返回值:交出来的数据;
- 打印:只是显示,不等于返回。
9.2 多做“把一段代码抽成函数”的练习
例如把这些内容单独写成函数:
- 计算成绩等级;
- 判断闰年;
- 验证用户名是否合法;
- 统计列表最大值与最小值;
- 格式化姓名或手机号。
比起死背语法,这种训练更能真正建立“函数思维”。
9.3 少依赖全局变量,多通过参数传值
如果一个函数需要什么数据,你最好明确让它通过参数拿到,而不是隐含依赖外部状态。这样写出来的函数会更清晰。
9.4 记住默认参数的可变对象陷阱
这是面试、实战、源码阅读里都非常高频的问题。你不一定一开始就能完全吃透,但至少要记住:
- 默认参数不要直接写
[]、{}这类可变对象; - 更稳妥的写法通常是
None。
十、总结
这一篇我们系统梳理了 Python 函数的基础知识:
- 函数的作用:减少重复、组织逻辑、提升复用性;
- 函数定义:通过
def创建函数; - 常见参数形式:位置参数、默认参数、关键字参数;
- 返回值机制:
return才是真正把结果交给后续代码; - 作用域:理解局部变量、全局变量与函数边界;
- 常见陷阱:尤其是默认参数使用可变对象的问题;
- 代码组织思路:把一个完整任务拆成多个职责清晰的小函数。
如果说上一章的基础语法解决的是“怎么把一段逻辑写出来”,那么函数这一章解决的就是:
如何把代码从“堆在一起的脚本”整理成“有结构、可复用的程序”。
把函数学扎实以后,你后面学模块、面向对象、装饰器、框架开发时都会轻松很多。因为那些更复杂的抽象,本质上都离不开对函数的理解。
下一步继续往下学时,你可以开始接触 Python 更有代表性的表达方式,比如切片、迭代、列表生成式、生成器和迭代器,也就是接下来会讲到的高级特性。