Python函数入门指南:定义、参数传递与返回值基础

Dylan
Dylan
发布时间:2026-11-09 | 12人浏览过
原创

系统讲解Python函数基础:从def语法定义、Docstring文档编写,到位置参数、默认参数与关键字参数的传递机制与按引用传递本质,再到return返回值与多值解包,附清晰技术配图与避坑指南。

在 Python 编程中,函数是将重复代码模块化、增强系统可读性与可维护性的基础构造。编写高质量函数的关键在于透彻理解三个核心环节:使用 def 正确声明函数接口,清晰掌握形参、实参、默认值与关键字传参的匹配规则,以及熟练运用 return 语句返回计算结果或控制代码退出。本文将从语法规范、内存传递本质到实际避坑逐层拆解,助你打牢函数编程的底层功底。
Python函数入门指南:定义、参数传递与返回值基础
缩略图:Python 函数三大核心要素全览(定义 · 传递 · 返回)

一、函数的基本定义与调用流程

在 Python 中,定义一个函数需要使用 def 关键字,后接函数名、圆括号包含的形参列表(可为空)以及一个冒号。函数体所有语句必须保持统一缩进(PEP 8 推荐 4 个空格)。定义完成后,通过“函数名(实参)”发起调用。

为了让代码具备良好的自解释性,规范的做法是在函数体首行编写三引号包裹的文档字符串(Docstring),简明扼要地交代函数的作用、参数类型及返回值。

代码示例:定义与调用基础函数
def calculate_rectangle_area(width, height):
    """计算矩形面积并返回结果。
    
    参数:
        width (float): 矩形的宽度
        height (float): 矩形的高度
        
    返回:
        float: 矩形计算所得的面积
    """
    area = width * height
    return area

# 示例调用:传入两个数值实参
result = calculate_rectangle_area(5.0, 3.2)
print("计算所得面积:", result)
示例运行输出
计算所得面积: 16.0

理解函数在解释器内部的流转步骤非常重要:当 Python 解释器扫描到 def 语句块时,它仅完成语法解析并创建一个函数对象(Function Object),将其指针绑定到函数名变量上,并不会执行函数体内的代码。只有当程序显式调用该函数时,Python 才会开辟新的调用栈帧并执行具体逻辑。

Python函数入门指南:定义、参数传递与返回值基础
图 1:Python 函数定义与调用生命周期执行流(阶段拆解与栈帧管理)
命名规范提醒
根据 PEP 8 规范,Python 函数名应全部采用小写字母加下划线的蛇形命名法(snake_case),例如 get_user_info、send_email,并尽量使用“动词”或“动宾短语”,切忌使用拼音简写或无意义的模糊命名。

二、参数传递:形参、实参与传参机制

在讨论参数传递前,首先需要分清两个基本概念:

  • 形参(Parameter):函数定义时括号中的变量名,代表该接口需要占位接收的数据。
  • 实参(Argument):函数被实际调用时,外部真实传入并绑定的具体对象或表达式。

在 Python 中,实参与形参的对应支持三种常见方式:位置参数、关键字参数和默认参数。

1. 位置参数与关键字参数

位置参数是默认的匹配方式,实参与形参依照严格的书写前后位置一一对应,数量必须严格一致;而关键字参数则允许在调用时显式指定形参名称(如 func(name="Alice", age=20)),由于明确了变量名,关键字参数可以完全颠倒顺序传入,极大提升了多参数调用时的可读性。

代码示例:位置参数与关键字参数对比
def build_user_profile(username, user_id, role="guest"):
    return f"用户: {username} (ID: {user_id}), 权限组: {role}"

# 1. 纯位置参数调用(必须依序)
print(build_user_profile("zhangsan", 1001, "admin"))

# 2. 纯关键字参数调用(顺序可任意调整)
print(build_user_profile(user_id=1002, role="editor", username="lisi"))

# 3. 混合调用(位置实参必须在关键字实参之前)
print(build_user_profile("wangwu", 1003, role="viewer"))
示例运行输出
用户: zhangsan (ID: 1001), 权限组: admin
用户: lisi (ID: 1002), 权限组: editor
用户: wangwu (ID: 1003), 权限组: viewer
Python函数入门指南:定义、参数传递与返回值基础
图 2:Python 形参与实参分类与语法匹配规则矩阵(核心规则与正误对照)

2. 语法排列约束

Python 解释器在语法层面设立了严格的排序法则:

  • 定义函数时:任何带有默认值的形参(如 b=10),必须统一放置在所有无默认值形参之后。违背该规则将直接触发 SyntaxError: non-default argument follows default argument。
  • 调用函数时:位置实参必须始终位于关键字实参的前面。如果尝试写出 func(a=1, 2),将直接报 SyntaxError: positional argument follows keyword argument。
高危陷阱:切勿使用可变对象(列表/字典)作为默认参数!
在 Python 中,函数定义中的默认参数表达式仅在函数定义被编译加载时求值一次,而不是每次调用重新生成。如果将列表 [] 或字典 {} 作为默认参数,所有未传入该实参的调用将永久共享同一份对象,从而导致数据跨调用累积污染。
代码示例:可变默认参数陷阱与标准修复姿势
# 错误示范:每次调用将累加同一默认列表
def bad_append(item, target_list=[]):
    target_list.append(item)
    return target_list

print("错误调用1:", bad_append("A"))
print("错误调用2:", bad_append("B"))  # 输出 ['A', 'B'],出现意外残留!

# 标准正解:使用 None 作为缺省占位,并在函数内部安全初始化
def safe_append(item, target_list=None):
    if target_list is None:
        target_list = []  # 每次调用独立创建全新的局部列表
    target_list.append(item)
    return target_list

print("安全调用1:", safe_append("A"))
print("安全调用2:", safe_append("B"))  # 独立输出 ['B'],互不干扰
示例运行输出
错误调用1: ['A']
错误调用2: ['A', 'B']
安全调用1: ['A']
安全调用2: ['B']

三、深入理解:Python“按对象引用传递”机制

初学 Python 常会产生疑问:“Python 究竟是按值传递还是按引用传递?”严格来说,Python 的传递模式被称为按对象引用传递(Call by Object Reference)或共享传递(Call by Sharing)。

在函数调用发生时,实参赋值给形参的实质是将外部变量所指向对象的内存地址(引用)复制给形参。但函数内部对该参数的后续操作,会因为对象本身的“可变性(Mutability)”展现出完全截然不同的行为:

对象类别 常见数据类型 函数内部操作表现 是否影响外部实参
不可变对象 int, float, str, tuple, bool 对形参执行运算或重新赋值时,Python 在堆内存中开辟新对象并将形参指针移至新对象。 绝不影响。外部变量依然牢牢指向原有原始对象。
可变对象 list, dict, set 若调用原地修改方法(如 .append(), .pop()),将直接改写共享堆内存中的数据。 直接影响。外部实参能观测到所有原地变更(产生副作用)。
可变对象(重新绑定) list, dict, set 若在函数内直接写 lst = [1, 2],属于指针重新绑定,而非原地修改。 不影响。仅局部变量改绑,外部原始对象完好无损。
Python函数入门指南:定义、参数传递与返回值基础
图 3:Python 参数传递底层内存指向对比(不可变对象改绑 vs 可变对象原地修改)

四、返回值基础:return 语句核心用法

在 Python 函数中,return 语句承担着两项截然不同的职责:

  1. 产出计算结果:将运算得到的数据对象交付回调用现场,赋予变量继续后续使用。
  2. 控制流程分支:一旦执行到 return,该函数立即终止运行并退出栈帧,其后的任何后续代码均被忽略。

1. 显式返回与隐式返回 None

如果函数执行完最后一行代码依然没有遇到 return,或者写了不带任何表达式的单独 return,Python 会隐式默认返回 None。这是 Python 处理无返回值纯动作类函数的通用惯例。

代码示例:返回值接收与 None 表现
def pure_action():
    print(">> 执行纯屏幕输出动作")
    # 未书写 return

res = pure_action()
print("接收返回值:", res)
print("是否严格为 None:", res is None)
示例运行输出
>> 执行纯屏幕输出动作
接收返回值: None
是否严格为 None: True

2. 多值返回与元组自动解包

在很多业务场景下,函数需要同时交付多个结果(例如统计中的最小值与最大值、数据处理的状态码与消息文本)。Python 原生支持用逗号分隔返回多个对象,其底层机制是自动封装为一个元组(Tuple)。

代码示例:多值返回与多元解包
def get_dataset_summary(numbers):
    if not numbers:
        return 0, 0, 0
    total = sum(numbers)
    average = total / len(numbers)
    count = len(numbers)
    return total, average, count  # 实际返回一个三元元组 (total, average, count)

# 方式 A:使用等量变量直接进行元组解包(推荐)
sum_val, avg_val, num_count = get_dataset_summary([10, 20, 30, 40])
print(f"求和: {sum_val}, 均值: {avg_val}, 样本量: {num_count}")

# 方式 B:用单个元组变量整体承接
summary_tuple = get_dataset_summary([10, 20, 30, 40])
print("完整元组对象:", summary_tuple)
示例运行输出
求和: 100, 均值: 25.0, 样本量: 4
完整元组对象: (100, 25.0, 4)

3. 提前返回与守卫子句(Guard Clauses)

在编写具备复杂入参校验的函数时,很多初学者喜欢使用多层嵌套的 if-else 结构,这容易导致代码向右倾斜、逻辑难以阅读。利用 return 可以编写守卫子句:在函数入口优先检测异常与非法条件并直接 return 退出,将主业务流程保持在平铺结构中。

代码示例:使用提前返回消灭深层嵌套
def process_payment(account, amount):
    # 守卫子句 1: 检查账号状态
    if not account.get("is_active"):
        return False, "账户未激活"
        
    # 守卫子句 2: 校验扣款金额
    if amount <= 0:
        return False, "交易金额必须大于零"
        
    # 守卫子句 3: 余额判定
    if account.get("balance", 0) < amount:
        return False, "余额不足"

    # 主核心逻辑(完全无嵌套进缩进阶梯)
    account["balance"] -= amount
    return True, "支付成功"
Python函数入门指南:定义、参数传递与返回值基础
图 4:Python 函数返回值机制与控制流分流(单值、None、元组打包与提前退出)

五、核心规范总结与常见避坑指南

掌握了函数的定义、参数传递与返回值之后,在实际项目编码中建议牢记以下四项黄金准则,并参考下方的全景看板进行快速排查:

  1. 分清 print 与 return 的本质区别:print() 仅是向标准输出流输出字符,供人肉眼查看,不能作为下游代码的输入;而 return 才是真正把数据移交给调用程序的核心语法。
  2. 严格遵守参数编写顺序:定义时无默认参数在前、有默认参数在后;调用时位置实参在前、关键字实参在后。
  3. 严禁使用可变类型作为默认形参:务必使用 arg=None 搭配内部条件初始化,杜绝跨调用污染。
  4. 明确返回值接口契约:同一个函数应尽量保持返回值类型的一致性,便于调用方做类型推导与稳定性判断。
Python函数入门指南:定义、参数传递与返回值基础
图 5:Python 函数基础核心规范与高频避坑全景看板(随查随用参考指南)

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表