最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

26. Python函数值传递和引用传递详解

网站源码admin1浏览0评论

26. Python函数值传递和引用传递详解

一、Python函数基础知识

1. 什么是函数?

函数是Python中可重复使用的代码块,它接收输入(参数),执行特定任务,并可能返回结果。函数帮助我们组织代码,提高代码复用性和可维护性。

2. 函数的定义和调用
代码语言:javascript代码运行次数:0运行复制
# 定义函数
def 打招呼(名字):
    """这是函数的文档字符串,用于说明函数功能"""
    return f"你好,{名字}!"

# 调用函数
结果 = 打招呼("小明")
print(结果)  # 输出:你好,小明!
3. 函数参数的类型
代码语言:javascript代码运行次数:0运行复制
# 位置参数
def 计算矩形面积(长, 宽):
    return 长 * 宽

print(计算矩形面积(5, 3))  # 输出:15

# 默认参数
def 打招呼(名字, 问候语="你好"):
    return f"{问候语},{名字}!"

print(打招呼("小明"))  # 输出:你好,小明!
print(打招呼("小红", "早上好"))  # 输出:早上好,小红!

# 关键字参数
print(计算矩形面积(宽=3, 长=5))  # 输出:15

# 可变参数
def 计算总和(*数字):
    return sum(数字)

print(计算总和(1, 2, 3, 4, 5))  # 输出:15

# 关键字可变参数
def 创建个人信息(**信息):
    return 信息

个人资料 = 创建个人信息(姓名="小明", 年龄=18, 城市="北京")
print(个人资料)  # 输出:{'姓名': '小明', '年龄': 18, '城市': '北京'}

二、形参与实参

在讨论函数参数传递机制前,我们需要理解两个重要概念:

1. 形参(Parameter)

形参是函数定义时声明的变量,它在函数定义时创建,函数结束时销毁。

2. 实参(Argument)

实参是函数调用时传递给函数的实际值,它在函数调用前就已经存在。

代码语言:javascript代码运行次数:0运行复制
def 打招呼(名字):  # 这里的"名字"是形参
    return f"你好,{名字}!"

人名 = "小明"  # 这是一个变量
结果 = 打招呼(人名)  # 这里的"人名"是实参

三、Python的参数传递机制

1. Python的对象模型

在Python中,一切皆为对象。每个对象都有三个特性:

  • 标识(Identity):对象在内存中的地址,可通过id()函数获取
  • 类型(Type):对象的类型,决定了对象可以进行的操作,可通过type()函数获取
  • 值(Value):对象包含的数据
2. 可变对象与不可变对象

Python中的对象分为两类:

不可变对象(Immutable)

  • 整数(int)
  • 浮点数(float)
  • 字符串(str)
  • 元组(tuple)
  • 布尔值(bool)

可变对象(Mutable)

  • 列表(list)
  • 字典(dict)
  • 集合(set)

这个区别对理解参数传递机制至关重要!

3. Python的参数传递机制:对象引用传递

Python中的参数传递机制实际上是对象引用传递(Pass by Object Reference)。这意味着:

  1. 函数参数在传递时,传递的是对象的引用(内存地址)
  2. 对于不可变对象,如果在函数内修改参数的值,会创建一个新对象
  3. 对于可变对象,在函数内的修改会直接影响原对象

四、不可变对象的参数传递(看起来像值传递)

当我们传递不可变对象(如整数、字符串)作为参数时,由于这些对象不能被修改,所以在函数内对参数的修改不会影响到原始对象。这种行为看起来像是值传递。

代码语言:javascript代码运行次数:0运行复制
def 修改数字(数值):
    print(f"函数内修改前,数值的ID:{id(数值)}")
    数值 += 10  # 这会创建一个新对象!
    print(f"函数内修改后,数值的ID:{id(数值)}")
    return 数值

原始数字 = 5
print(f"函数调用前,原始数字的ID:{id(原始数字)}")

结果 = 修改数字(原始数字)

print(f"函数调用后,原始数字:{原始数字}")
print(f"函数调用后,结果:{结果}")
print(f"函数调用后,原始数字的ID:{id(原始数字)}")
print(f"函数调用后,结果的ID:{id(结果)}")

输出结果类似:

代码语言:javascript代码运行次数:0运行复制
函数调用前,原始数字的ID:140704726601296
函数内修改前,数值的ID:140704726601296
函数内修改后,数值的ID:140704726601616
函数调用后,原始数字:5
函数调用后,结果:15
函数调用后,原始数字的ID:140704726601296
函数调用后,结果的ID:140704726601616

解释

  1. 原始数字和函数参数最初指向同一个对象(ID相同)
  2. 当我们在函数内执行 数值 += 10 时,由于整数是不可变的,Python创建了一个新的整数对象
  3. 函数返回的是新创建的对象
  4. 原始数字保持不变
字符串示例
代码语言:javascript代码运行次数:0运行复制
def 修改字符串(文本):
    print(f"函数内修改前,文本的ID:{id(文本)}")
    文本 += "世界"  # 创建新字符串对象
    print(f"函数内修改后,文本的ID:{id(文本)}")
    return 文本

原始文本 = "你好"
print(f"函数调用前,原始文本的ID:{id(原始文本)}")

结果 = 修改字符串(原始文本)

print(f"函数调用后,原始文本:{原始文本}")
print(f"函数调用后,结果:{结果}")

五、可变对象的参数传递(看起来像引用传递)

当我们传递可变对象(如列表、字典)作为参数时,函数内对参数的修改会直接影响原始对象。这种行为看起来像是引用传递。

代码语言:javascript代码运行次数:0运行复制
def 修改列表(列表):
    print(f"函数内修改前,列表的ID:{id(列表)}")
    列表.append(4)  # 直接修改原列表
    print(f"函数内修改后,列表的ID:{id(列表)}")
    return 列表

原始列表 = [1, 2, 3]
print(f"函数调用前,原始列表的ID:{id(原始列表)}")

结果 = 修改列表(原始列表)

print(f"函数调用后,原始列表:{原始列表}")
print(f"函数调用后,结果:{结果}")
print(f"函数调用后,原始列表的ID:{id(原始列表)}")
print(f"函数调用后,结果的ID:{id(结果)}")

输出结果类似:

代码语言:javascript代码运行次数:0运行复制
函数调用前,原始列表的ID:140704726789632
函数内修改前,列表的ID:140704726789632
函数内修改后,列表的ID:140704726789632
函数调用后,原始列表:[1, 2, 3, 4]
函数调用后,结果:[1, 2, 3, 4]
函数调用后,原始列表的ID:140704726789632
函数调用后,结果的ID:140704726789632

解释

  1. 原始列表和函数参数指向同一个对象(ID相同)
  2. 当我们在函数内执行 列表.append(4) 时,直接修改了原始列表对象
  3. 函数返回的仍然是原始列表对象
  4. 原始列表被修改
字典示例
代码语言:javascript代码运行次数:0运行复制
def 修改字典(字典):
    字典["年龄"] = 25  # 直接修改原字典
    return 字典

原始字典 = {"姓名": "小明", "年龄": 18}
结果 = 修改字典(原始字典)

print(f"函数调用后,原始字典:{原始字典}")
print(f"函数调用后,结果:{结果}")

六、混合情况:可变对象中的不可变对象

代码语言:javascript代码运行次数:0运行复制
def 修改嵌套结构(数据):
    # 修改列表中的第一个元素(不可变对象)
    数据[0] = 100  # 这会修改原列表
    
    # 尝试修改元组中的元素
    try:
        数据[1][0] = 200  # 这会失败,因为元组是不可变的
    except TypeError as e:
        print(f"错误:{e}")
    
    # 修改列表中的字典(可变对象)
    数据[2]["分数"] = 95  # 这会修改原字典
    
    return 数据

原始数据 = [1, (2, 3), {"姓名": "小明", "分数": 85}]
结果 = 修改嵌套结构(原始数据)

print(f"函数调用后,原始数据:{原始数据}")
print(f"函数调用后,结果:{结果}")

七、避免可变对象作为默认参数

Python中一个常见的陷阱是使用可变对象作为函数的默认参数:

代码语言:javascript代码运行次数:0运行复制
# 错误示例
def 添加学生(姓名, 班级=[]):
    班级.append(姓名)
    return 班级

班级1 = 添加学生("小明")
print(班级1)  # 输出:["小明"]

班级2 = 添加学生("小红")
print(班级2)  # 输出:["小明", "小红"] - 可能不是预期结果!

解释:默认参数在函数定义时创建,而不是函数调用时。对于可变对象,这意味着所有函数调用都共享同一个默认对象。

正确做法

代码语言:javascript代码运行次数:0运行复制
# 正确示例
def 添加学生(姓名, 班级=None):
    if 班级 is None:
        班级 = []
    班级.append(姓名)
    return 班级

班级1 = 添加学生("小明")
print(班级1)  # 输出:["小明"]

班级2 = 添加学生("小红")
print(班级2)  # 输出:["小红"]

八、实际应用示例

1. 函数返回多个值
代码语言:javascript代码运行次数:0运行复制
def 计算统计值(数字列表):
    """计算列表的最小值、最大值和平均值"""
    最小值 = min(数字列表)
    最大值 = max(数字列表)
    平均值 = sum(数字列表) / len(数字列表)
    return 最小值, 最大值, 平均值

数据 = [4, 7, 2, 9, 5]
最小, 最大, 平均 = 计算统计值(数据)
print(f"最小值:{最小},最大值:{最大},平均值:{平均}")
2. 函数作为参数传递
代码语言:javascript代码运行次数:0运行复制
def 应用操作(函数, 数据):
    """将函数应用于数据"""
    return 函数(数据)

def 平方(x):
    return x ** 2

def 立方(x):
    return x ** 3

数字 = 5
print(f"{数字}的平方是:{应用操作(平方, 数字)}")
print(f"{数字}的立方是:{应用操作(立方, 数字)}")

九、总结

  1. Python的参数传递机制是对象引用传递:传递的是对象的引用,而不是对象的副本或直接引用。
  2. 不可变对象(如整数、字符串、元组):
    • 在函数内修改参数值会创建新对象
    • 原始对象保持不变
    • 行为类似于值传递
  3. 可变对象(如列表、字典、集合):
    • 在函数内修改参数会直接影响原始对象
    • 原始对象会被修改
    • 行为类似于引用传递
  4. 避免使用可变对象作为默认参数,这可能导致意外行为。
  5. 理解对象的可变性对于编写正确的Python代码至关重要。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-28,如有侵权请联系 cloudcommunity@tencent 删除python对象函数数据字符串
发布评论

评论列表(0)

  1. 暂无评论