Python函数基础:定义、参数、返回值与作用域入门

当我们刚学完 Python 的基础语法之后,往往已经可以写出一些简单的小脚本了:读取输入、做判断、写循环、处理列表和字典。可是只要代码一多,你很快就会发现一个问题:逻辑开始重复,代码开始变长,维护也开始变麻烦。

这时候,函数就该出场了。

函数的价值,不只是“把几行代码包起来”这么简单。更重要的是,它能帮我们把一段逻辑起一个明确的名字,让代码结构从“顺着往下写的脚本”变成“可以拆分、复用、组合的模块”。这一点,在后面学习模块、面向对象、Web 开发时都会不断体现出来。

本文就围绕 Python 中最基础也最重要的函数知识,系统讲清楚以下几个问题:

  • 什么是函数,为什么要用函数;
  • 如何定义函数;
  • 参数有哪些常见写法;
  • 返回值到底是怎么工作的;
  • 局部变量与作用域该怎么理解;
  • 编写函数时有哪些常见陷阱与建议。

一、为什么要学函数?

先看一个最直观的例子。假设我们需要多次计算矩形面积:

1
2
3
4
5
6
7
8
9
length1 = 5
width1 = 3
area1 = length1 * width1
print(area1)

length2 = 8
width2 = 6
area2 = length2 * width2
print(area2)

这段代码当然能运行,但问题也很明显:

  1. 逻辑重复
  2. 如果以后计算方式要改,得改很多处;
  3. 代码含义不够集中,看起来像“过程”,不像“能力”。

如果改成函数:

1
2
3
4
5
def calculate_area(length, width):
return length * width

print(calculate_area(5, 3))
print(calculate_area(8, 6))

一下就清晰了很多。

你可以把函数理解成:

给一段可复用的逻辑起名字,并且允许我们在需要的时候传入数据、得到结果。

函数最核心的好处主要有三点:

  • 减少重复代码
  • 让程序结构更清晰
  • 更容易测试、维护和扩展

很多初学者前期写代码,最大的问题不是语法不会,而是所有逻辑都堆在一起。函数就是从“会写脚本”走向“会组织代码”的第一步。


二、函数的定义与调用

2.1 最基本的函数写法

在 Python 中,函数通过 def 关键字定义:

1
2
def greet(name):
print(f'你好,{name}')

这段代码的意思是:

  • def 表示定义函数;
  • greet 是函数名;
  • name 是参数;
  • 缩进部分是函数体。

调用时这样写:

1
2
greet('Alice')
greet('Bob')

输出:

1
2
你好,Alice
你好,Bob

这里要特别注意:

  • greet 表示函数本身;
  • greet('Alice') 才表示调用函数。
2.2 函数名应该怎么起?

函数名建议遵循下面这些规则:

  1. 使用小写字母;
  2. 多个单词之间使用下划线分隔;
  3. 尽量让名字表达出行为,而不是模糊描述。

例如:

1
2
3
4
5
6
7
8
9
10
def get_user_name():
pass


def validate_email():
pass


def send_message():
pass

比起 foo()test1()do_it() 这类名字,可读性会高很多。


三、参数:函数如何接收外部数据

函数之所以有用,很大程度上就在于它能处理不同输入。

3.1 位置参数

最常见的是位置参数。

1
2
3
4
def add(a, b):
return a + b

print(add(3, 5))

调用时 3 会传给 a5 会传给 b

这种写法要求:参数顺序要对应。

1
print(add(5, 3))

这时候虽然结果还是 8,但对于减法、除法等函数来说,顺序就会直接影响结果。

3.2 默认参数

有些参数在大多数情况下取值固定,就可以给它设置默认值。

1
2
3
4
5
6
def greet(name, greeting='你好'):
print(f'{greeting}{name}')


greet('小王')
greet('Alice', 'Hello')

输出:

1
2
你好,小王
Hello,Alice

默认参数的意义在于:

  • 常用场景可以少传一个参数;
  • 接口更友好;
  • 函数更容易复用。

不过有两个规则要记住:

  1. 默认参数要放在非默认参数后面;
  2. 默认值最好使用不可变对象。

例如:

1
2
def register(username, role='user'):
print(username, role)

这是合法且常见的写法。

3.3 关键字参数

调用函数时,我们还可以通过“参数名=值”的方式传参。

1
2
3
4
5
def introduce(name, age, city):
print(f'我叫{name},今年{age}岁,来自{city}')


introduce(age=20, city='北京', name='小李')

关键字参数的好处是:

  • 可读性更好;
  • 不容易把顺序写错;
  • 某些参数较多的函数更适合这种写法。
3.4 位置参数和关键字参数可以混用吗?

可以,但要遵守规则:

  • 位置参数必须写在前面;
  • 关键字参数写在后面。

例如:

1
2
3
4
5
def create_user(name, age, city='上海'):
print(name, age, city)


create_user('Tom', 18, city='杭州')

这是合法的。

但如果把关键字参数写前面、位置参数写后面,就会报错。


四、返回值:函数执行完之后把结果交回来

很多初学者一开始会把“打印结果”和“返回结果”混在一起,这两者其实完全不是一回事。

4.1 print() 不等于返回值

先看这个函数:

1
2
def add(a, b):
print(a + b)

调用:

1
2
result = add(2, 3)
print(result)

输出会是:

1
2
5
None

为什么?

因为这个函数只是把结果打印出来了,但没有返回结果。所以变量 result 拿到的是 None

4.2 正确返回结果要用 return
1
2
3
4
5
6
def add(a, b):
return a + b


result = add(2, 3)
print(result)

这时候 result 才真正拿到了 5

你可以这样理解:

  • print() 是“展示给人看”;
  • return 是“交给程序后续继续使用”。

在真实开发里,函数更重要的是 return,因为返回值才意味着函数可以继续参与后面的逻辑。

4.3 一个函数可以有多个 return
1
2
3
4
5
6
def compare(a, b):
if a > b:
return 'a 更大'
if a < b:
return 'b 更大'
return '两者相等'

函数会在遇到 return 时立刻结束执行。

4.4 不写 return 会怎样?

如果函数没有显式写 return,那么默认返回 None

1
2
3
4
5
def say_hello(name):
print(f'Hello, {name}')


print(say_hello('Python'))

输出最后会多出一个 None

这不是错误,而是 Python 的默认行为。

4.5 返回多个值是怎么回事?

Python 常说“一个函数可以返回多个值”,其实本质上返回的是一个元组。

1
2
3
4
5
6
7
8
9
def get_user_info():
name = 'Alice'
age = 20
city = 'Shanghai'
return name, age, city


result = get_user_info()
print(result)

输出:

1
('Alice', 20, 'Shanghai')

也可以直接拆包:

1
2
3
4
name, age, city = get_user_info()
print(name)
print(age)
print(city)

这种写法在 Python 中很常见,比如后面处理文件、坐标、配置项时都会用到。


五、作用域:函数里的变量和外面的变量是什么关系?

函数不仅仅涉及参数和返回值,还涉及一个非常重要的概念:变量作用域

5.1 局部变量

定义在函数内部的变量,默认只在函数内部有效。

1
2
3
4
5
6
7
def demo():
message = 'hello'
print(message)


demo()
# print(message) # 会报错

这里的 message 是局部变量,出了函数就不能直接访问。

5.2 全局变量

定义在函数外部的变量,通常称为全局变量。

1
2
3
4
5
6
7
8
name = 'Python'


def show_name():
print(name)


show_name()

这种情况下,函数内部可以读取外部变量。

5.3 为什么要强调作用域?

因为如果你对变量到底来自哪里不清楚,代码一复杂就很容易乱。

一个很常见的坏习惯是:

  • 函数大量依赖外部变量;
  • 参数传得很少;
  • 看函数本身根本不知道它需要什么上下文。

更推荐的方式是:

  • 优先通过参数传入需要的数据;
  • 通过返回值交出结果;
  • 少让函数偷偷依赖外部环境。

这样函数会更独立,也更容易测试。

5.4 修改全局变量为什么要小心?

如果你在函数内部给一个和全局变量同名的变量赋值,Python 会优先把它当成局部变量。

1
2
3
4
5
6
7
8
9
10
count = 10


def change_count():
count = 20
print(count)


change_count()
print(count)

输出:

1
2
20
10

说明函数内部的 count 和外部的 count 其实不是同一个变量。

如果确实要在函数里修改全局变量,可以使用 global,但初学阶段不建议滥用。

1
2
3
4
5
6
7
8
9
10
count = 10


def change_count():
global count
count = 20


change_count()
print(count)

虽然这样能改成功,但从代码设计角度看,函数频繁修改全局状态通常不是好习惯


六、一个非常容易踩坑的问题:默认参数不要乱用可变对象

这是 Python 函数里非常经典的一个坑。

先看代码:

1
2
3
4
5
6
7
8
def add_item(item, items=[]):
items.append(item)
return items


print(add_item('a'))
print(add_item('b'))
print(add_item('c'))

很多人以为输出会是:

1
2
3
['a']
['b']
['c']

但实际输出是:

1
2
3
['a']
['a', 'b']
['a', 'b', 'c']

原因在于:默认参数只在函数定义时创建一次。items=[] 这个列表不会在每次调用时重新生成,而是被反复复用。

正确写法通常是:

1
2
3
4
5
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items

这是 Python 里非常常见、也非常值得记住的写法。


七、函数文档与注释:让函数更容易被别人理解

当函数开始变多之后,仅靠名字还不一定足够。此时可以使用文档字符串来补充说明。

1
2
3
def calculate_bmi(weight, height):
"""根据体重和身高计算 BMI 指数。"""
return weight / (height ** 2)

文档字符串的价值主要在于:

  • 告诉别人这个函数是干什么的;
  • 帮助 IDE 和 help() 输出说明;
  • 函数多了以后更容易维护。

如果函数稍复杂一点,还可以把参数和返回值说明写进去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def convert_score(score):
"""
将分数转换为等级。

参数:
score (int): 0 到 100 的整数分数

返回:
str: 对应的等级描述
"""
if score >= 90:
return '优秀'
if score >= 60:
return '及格'
return '不及格'

虽然你现在写的小函数可能觉得“没必要”,但一旦进入多人协作或过几个月再回头看自己的代码,就会发现这类说明真的很重要。


八、综合示例:把零散逻辑整理成函数

下面我们做一个简单的小练习:把学生成绩处理逻辑拆成多个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def get_level(score):
if score >= 90:
return '优秀'
if score >= 80:
return '良好'
if score >= 60:
return '及格'
return '不及格'


def is_pass(score):
return score >= 60


def print_report(name, score):
level = get_level(score)
status = '通过' if is_pass(score) else '未通过'
print(f'学生:{name},分数:{score},等级:{level},结果:{status}')


print_report('小张', 92)
print_report('小李', 76)
print_report('小王', 48)

这个例子虽然简单,但已经体现出函数拆分的价值:

  • get_level() 负责等级判断;
  • is_pass() 负责判断是否及格;
  • print_report() 负责组织输出。

如果以后规则变化,比如“85 分以上就算优秀”,你只需要改其中一个函数,而不是全局到处找判断逻辑。

这就是函数带来的结构化收益。


九、学习建议:初学函数时该重点抓什么?

9.1 先分清参数、返回值、打印输出

这是最基础也最容易混的点:

  • 参数:传进去的数据;
  • 返回值:交出来的数据;
  • 打印:只是显示,不等于返回。
9.2 多做“把一段代码抽成函数”的练习

例如把这些内容单独写成函数:

  • 计算成绩等级;
  • 判断闰年;
  • 验证用户名是否合法;
  • 统计列表最大值与最小值;
  • 格式化姓名或手机号。

比起死背语法,这种训练更能真正建立“函数思维”。

9.3 少依赖全局变量,多通过参数传值

如果一个函数需要什么数据,你最好明确让它通过参数拿到,而不是隐含依赖外部状态。这样写出来的函数会更清晰。

9.4 记住默认参数的可变对象陷阱

这是面试、实战、源码阅读里都非常高频的问题。你不一定一开始就能完全吃透,但至少要记住:

  • 默认参数不要直接写 []{} 这类可变对象;
  • 更稳妥的写法通常是 None

十、总结

这一篇我们系统梳理了 Python 函数的基础知识:

  1. 函数的作用:减少重复、组织逻辑、提升复用性;
  2. 函数定义:通过 def 创建函数;
  3. 常见参数形式:位置参数、默认参数、关键字参数;
  4. 返回值机制return 才是真正把结果交给后续代码;
  5. 作用域:理解局部变量、全局变量与函数边界;
  6. 常见陷阱:尤其是默认参数使用可变对象的问题;
  7. 代码组织思路:把一个完整任务拆成多个职责清晰的小函数。

如果说上一章的基础语法解决的是“怎么把一段逻辑写出来”,那么函数这一章解决的就是:

如何把代码从“堆在一起的脚本”整理成“有结构、可复用的程序”。

把函数学扎实以后,你后面学模块、面向对象、装饰器、框架开发时都会轻松很多。因为那些更复杂的抽象,本质上都离不开对函数的理解。

下一步继续往下学时,你可以开始接触 Python 更有代表性的表达方式,比如切片、迭代、列表生成式、生成器和迭代器,也就是接下来会讲到的高级特性

参考资料