news 2026/10/16 12:49:55

Python入门篇之函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python入门篇之函数

前言


刚开始学 Python,最容易被两个问题卡住:一是「为什么要把代码写成函数」,二是「函数里的变量到底归谁管」。前者关系到代码能不能复用和维护,后者关系到你会不会在某个深夜突然收到NameError或UnboundLocalError。这篇入门篇不追求面面俱到,只把函数最基本的三件事讲透:怎么定义、怎么传参、怎么拿返回值。


常见的误解有三类。第一类是把函数当成「压缩代码行数的工具」,觉得几行代码直接写就行,何必包成函数——其实函数的价值在复用和命名,不在省行数。第二类是以为「函数里能随便改外面的变量」,结果一赋值就报错。第三类是带着 Python 2 的习惯写print x,在 Python 3 里直接语法报错。


顺便把版本讲清楚:Python 2.7 已于 2020 年 1 月 1 日停止维护。Python 2 里print是语句,可以写print "hello";Python 3 里print是函数,必须写print("hello")。同理 Python 2 的raw_input在 3 里改叫input,dict.iteritems()在 3 里已移除,要用dict.items()。本文一律用 Python 3 的写法。


一、函数是什么:给一段代码起名字


把完成某件事的代码写成一个函数,就是给这段逻辑起了个名字,以后用名字调用它即可:


# 适用于 Python 3.8+
def say_hello():
print("你好,欢迎学习 Python 函数")

say_hello()
say_hello()

这里的def是 define 的缩写。say_hello是函数名,()是参数表(这里为空),冒号后面缩进的部分是函数体。调用时写函数名加圆括号。


函数带来的直接好处是:逻辑只写一次,想用几次就调用几次;以后要改行为,只改函数体一处。这比到处复制粘贴可靠得多。


二、让函数接收数据:参数


没有参数的函数只能做固定的事。要让函数处理不同的数据,就在参数表里声明参数(parameter),调用时把数据(实参,argument)传进去:


# 适用于 Python 3.8+
def greet(name):
print(f"你好,{name}")

greet("小明") # 你好,小明
greet("小红") # 你好,小红

函数可以接收多个参数,按位置一一对应:


# 适用于 Python 3.8+
def introduce(name, age):
print(f"{name} 今年 {age} 岁")

introduce("小明", 18) # 小明 今年 18 岁

如果参数有合理的缺省情况,可以给默认值。默认值让调用方可以少传:


# 适用于 Python 3.8+
def greet(name, greeting="你好"):
print(f"{greeting},{name}")

greet("小明") # 你好,小明
greet("小明", "早上好") # 早上好,小明
greet("小明", greeting="嗨") # 嗨,小明

调用时既可以按位置传,也可以写成名字=值的形式(关键字参数)。但关键字参数必须写在位置参数后面。


一个必须记住的点:默认值只在函数定义时求值一次。如果默认值是可变的,比如列表或字典,就会出现「上一次调用的残留」。正确做法是用None当哨兵:


# 适用于 Python 3.8+
def add_item(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket

print(add_item("a")) # ['a']
print(add_item("b")) # ['b'],而不是 ['a', 'b']

三、拿回结果:return 与 None


函数做完事,可以用return把结果交回给调用方:


# 适用于 Python 3.8+
def add(a, b):
return a + b

total = add(3, 5)
print(total) # 8

return会立即结束函数,后面的代码不再执行:


# 适用于 Python 3.8+
def check(n):
if n < 0:
return "负数"
return "非负数"

print(check(-1)) # 负数
print(check(5)) # 非负数

如果函数没有return,或者写了光秃秃的return,它返回的是None。这是新手最容易忽略的地方:


# 适用于 Python 3.8+
def show(msg):
print(msg)

value = show("一条消息") # 打印:一条消息
print(value) # None

所以像lst.sort()这种原地排序的方法返回的是None,千万不要写result = lst.sort()。想让「排序后得到新列表」,用内置函数sorted():


# 适用于 Python 3.8+
data = [3, 1, 2]
new = sorted(data)
print(new) # [1, 2, 3]
print(data) # [3, 1, 2],原列表没变

return想一次给出多个结果时,把它们用逗号隔开,实际上会打包成元组,接收方可以用多个变量接:


# 适用于 Python 3.8+
def divmod_like(a, b):
return a // b, a % b

q, r = divmod_like(7, 3)
print(q, r) # 2 1

(顺便说,标准库的内置函数divmod(a, b)做的就是这件事,返回(a // b, a % b)。)


四、作用域:函数里的名字归谁管


函数体是一个新的作用域。函数内部可以读外层的变量,但一旦在里面给某个名字赋值,这个同名变量就会被当成函数的局部变量:


# 适用于 Python 3.8+
name = "全局"

def show():
print(name) # 读取外层变量,没问题

show() # 全局

# 适用于 Python 3.8+
count = 0

def bump():
count = count + 1 # 这里 count 被当作局部变量,自引用时报错

# bump() # UnboundLocalError: local variable 'count' referenced before assignment

要在函数里修改全局变量,必须用global声明;要修改外层函数的变量,用nonlocal:


# 适用于 Python 3.8+
count = 0

def bump():
global count
count += 1

bump()
bump()
print(count) # 2

新手记住一句话就够了:函数里看到赋值,默认就当局部变量。想动外面的,先声明。


实战:一个完整可运行的小程序


下面把定义、参数、默认值、返回值、作用域揉进一个小例子,可以直接复制进hello_functions.py运行。


# 适用于 Python 3.8+
def bmi(weight, height, /):
"""根据体重(千克)和身高(米)计算 BMI 指数。"""
if height <= 0:
return None
return weight / (height ** 2)

def grade(value, *, thresholds=(18.5, 24.0, 28.0)):
"""把 BMI 数值映射为中文档位,只接受关键字参数。"""
if value is None:
return "输入无效"
low, mid, high = thresholds
if value < low:
return "偏瘦"
if value < mid:
return "正常"
if value < high:
return "偏胖"
return "肥胖"

def report(weight, height):
value = bmi(weight, height)
if value is None:
print("身高必须大于 0")
return
print(f"BMI = {value:.1f},档位:{grade(value)}")

if __name__ == "__main__":
report(70, 1.75)
report(50, 1.60)
report(70, 0)

逐段说明:



  • def bmi(weight, height, /)里的/表示这两个参数是仅位置参数(Python 3.8 起支持),只能按位置传,不能写bmi(weight=70, height=1.75)。

  • def grade(value, *, thresholds=...)里的*表示后面的thresholds是仅关键字参数,调用时只能写thresholds=...。

  • 两个函数都有明确的返回;bmi在非法输入时返回None,grade对None单独处理。

  • report用f"..."格式化,{value:.1f}表示保留一位小数。


常见坑点



  1. 忘了调用,只写了函数名


❌def work(): ...之后写work—— 只是引用了函数对象,什么都不会执行。 ✅ 写work(),带上圆括号才是调用。



  1. 可变默认参数


❌def add(item, bucket=[]): bucket.append(item); return bucket—— 默认列表被所有调用共享。 ✅def add(item, bucket=None): bucket = [] if bucket is None else bucket; ...



  1. 把None当有效返回值用


❌if find_user(uid) > 0:—— 找不到时函数返回None,和数字比较会TypeError。 ✅ 先判断result = find_user(uid),再写if result is None: ...。



  1. 误以为sort()返回新列表


❌result = data.sort()——result是None,data已被原地排序。 ✅ 要新列表用result = sorted(data);要原地改就用data.sort()且不接收返回值。



  1. 函数内赋值忘了global/nonlocal


❌ 直接count += 1想改全局count,结果UnboundLocalError。 ✅ 先global count(外层函数则用nonlocal count),再count += 1。



  1. 关键字参数写在位置参数前面


❌greet(name="小明", "早上好")—— 位置参数跟在关键字参数后,SyntaxError。 ✅greet("小明", "早上好")或greet("小明", greeting="早上好")。



  1. 拿 Python 2 的语法写 Python 3


❌print "你好"、raw_input()、d.iteritems()—— 前者是 Python 2 语句,后两个在 Python 3 里已改名或移除。 ✅print("你好")、input()、d.items()。


总结




概念一句话要点



定义def 名字(参数):加缩进函数体

调用函数名加圆括号,别漏括号

参数位置参数、关键字参数、默认值;默认值定义时求值一次

可变默认值换成None哨兵,调用内新建

返回值无return返回None;多值返回是元组

作用域函数内赋值默认为局部;改外层要global/nonlocal



入门阶段把这几条理顺,写函数基本不会出大问题:先把「参数怎么进、返回值怎么出」想清楚,再记住「默认值只算一次」和「内部赋值即局部」这两条作用域规律。剩下的,等对函数更熟了,再去接触*args、**kwargs、闭包和装饰器这些进阶玩法。




版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 12:47:03

STM32自制飞控全攻略:从姿态解算到PID调参避坑

简介&#xff1a;一份基于STM32微控制器的无人机飞控开发资料&#xff0c;面向嵌入式开发者和无人机爱好者&#xff0c;适合用于课程设计、项目参考或从零理解飞控系统构建。资料共212个文件&#xff0c;整体约4.53MB&#xff0c;以C源码&#xff08;.c/.h&#xff09;为核心&a…

作者头像 李华
网站建设 2026/10/16 12:46:56

TOA定位算法详解:二维三维场景MATLAB仿真实现与精度分析

做无线定位有一阵子的人应该都有同感&#xff1a;真正让定位算法落地最难的不是推导公式&#xff0c;而是把公式批量跑成可视化的仿真结果。前阵子接了个模拟项目X的定位方案预研&#xff0c;需求很直接——设备上报信号到达时间&#xff0c;后端要在二维和三维两种场景里算出位…

作者头像 李华
网站建设 2026/10/16 12:46:48

Windows下Docker启动失败:虚拟化支持未检测到的完整排查指南

刚在 Windows 上装完 Docker Desktop 的人&#xff0c;大概率都有过这么一段经历&#xff1a;图标双击&#xff0c;小鲸鱼转了一会儿圈&#xff0c;任务栏图标变灰&#xff0c;鼠标挪上去一看——Windows 系统里弹出“Virtualization support not detected”。没头没尾&#xf…

作者头像 李华
网站建设 2026/10/16 12:46:15

恒温系统上位机源码开发:MODBUS通信与实时数据监控全流程解析

简介&#xff1a;恒温系统上位机源码以C#语言编写&#xff0c;面向工业自动化初学者和温控系统开发者&#xff0c;用于解决环境或设备恒温控制中上位机与下位机之间的数据采集、实时显示、命令下发及控制策略设定等问题。压缩包共40个文件&#xff0c;大小仅128KB&#xff0c;以…

作者头像 李华
网站建设 2026/10/16 12:45:52

流式输出原理与实战:SSE、前端渲染和避坑指南

流式输出这东西&#xff0c;说实话&#xff0c;我第一次意识到它有多重要&#xff0c;是在一次糟糕的等待体验里。那时候还不流行大模型&#xff0c;但已经在做类似机器翻译的接口。用户点一下按钮&#xff0c;要等三到五秒才看到完整结果。当时有人在页面上加了一个转圈动画&a…

作者头像 李华
网站建设 2026/10/16 12:45:24

dea solver的Matlab实现:从效率评价原理到CCR/BCC模型实战

简介&#xff1a;这是一份面向运筹学、管理科学研究者与学生的小型DEA&#xff08;数据包络分析&#xff09;MATLAB程序包&#xff0c;内含DEA Solver工具箱&#xff0c;可用于CCR、BCC等多种经典模型的快速构建与求解&#xff0c;解决多输入多输出系统的相对效率评估问题。压缩…

作者头像 李华