幻世域-公会争霸活动网


这是一个非常核心且重要的 Python 概念。理解参数和返回值的传递机制,是从“会写代码”到“写好代码”的关键一步。

我们可以用一个简单的比喻来理解:函数就像一个功能专一的加工机器。

参数 (Argument):是你投入机器的原材料。函数体 (Function Body):是机器内部的加工过程。返回值 (Return Value):是机器加工完成后吐出的成品。

现在,我们来深入探讨这个“投入”和“产出”的过程是如何在 Python 中实现的。

参数 (Argument):数据如何进入函数

参数是将数据从外部传递到函数内部的通道。当你调用一个函数时,你提供的值就是参数。

核心传递机制:赋值传递 (Pass by Assignment)

Python 的参数传递方式既不同于 C++ 的“值传递”,也不同于其“引用传递”。最准确的描述是赋值传递 (Pass by Assignment) 或 对象引用传递 (Pass by Object Reference)。

理解这个机制的关键在于记住 Python 的一句话:“万物皆对象”。变量名本身不是数据的容器,而是一个指向数据对象的标签。

当你把一个变量作为参数传递给函数时,实际发生的是:

在函数内部,创建了一个新的标签(函数内的局部变量),让它指向外部标签所指向的同一个对象。

这个机制对不同类型的对象会产生看起来不同的效果:

1. 当传递不可变对象 (Immutable Objects) 时

像数字 (int, float)、字符串 (str)、元组 (tuple) 这类对象,其自身的值是不能被改变的。

def try_to_change_number(num):

print(f"函数开始时,num 的 ID: {id(num)}") # 查看对象内存地址

num = num + 10 # 这里的关键!

print(f"函数结束时,num 的 ID: {id(num)}")

x = 5

print(f"调用前,x 的 ID: {id(x)}")

try_to_change_number(x)

print(f"调用后,x 的值: {x}") # x 的值并没有改变

# 输出:

# 调用前,x 的 ID: 4488582064

# 函数开始时,num 的 ID: 4488582064 <-- x 和 num 指向同一个对象 5

# 函数结束时,num 的 ID: 4488582384 <-- num 指向了一个新的对象 15

# 调用后,x 的值: 5

发生了什么?

调用函数时,函数内的标签 num 指向了和外部标签 x 相同的对象 5。当执行 num = num + 10 时,因为数字 5 是不可变的,Python 无法在原地修改它。所以 Python 创建了一个新的数字对象 15。然后,函数内的标签 num 被重新赋值,转而指向了这个新的对象 15。整个过程,外部的标签 x 始终指向原来的对象 5,从未受影响。

所以,对于不可变对象,其效果看起来非常像“值传递”。

2. 当传递可变对象 (Mutable Objects) 时

像列表 (list)、字典 (dict)、集合 (set) 这类对象,其自身的内容是可以被修改的。

def change_list(my_list):

print(f"函数开始时,my_list 的 ID: {id(my_list)}")

my_list.append(4) # 这里的关键!

print(f"函数结束时,my_list 的 ID: {id(my_list)}")

items = [1, 2, 3]

print(f"调用前,items 的 ID: {id(items)}")

change_list(items)

print(f"调用后,items 的值: {items}") # items 的值被改变了!

# 输出:

# 调用前,items 的 ID: 4545224768

# 函数开始时,my_list 的 ID: 4545224768 <-- items 和 my_list 指向同一个列表对象

# 函数结束时,my_list 的 ID: 4545224768 <-- my_list 仍然指向同一个对象

# 调用后,items 的值: [1, 2, 3, 4]

发生了什么?

调用函数时,函数内的标签 my_list 指向了和外部标签 items 相同的列表对象。当执行 my_list.append(4) 时,我们没有对 my_list 这个标签进行重新赋值。我们是通过这个标签,直接修改了它所指向的那个列表对象本身的内容。因为外部的 items 和内部的 my_list 指向的是同一个对象,所以当这个对象被修改后,通过 items 也能看到这个变化。

所以,对于可变对象,其效果看起来非常像“引用传递”。

返回值 (Return Value):数据如何从函数出来

返回值是将数据从函数内部传递回外部调用者的通道。它是一个函数执行完毕后留下的“成果”。

核心传递机制:return 关键字

return 关键字做了两件事:

立即终止函数的执行。return 后面的任何代码都不会被执行。将 return 后面跟着的值(或对象引用)发送回调用该函数的地方。

def add_and_multiply(a, b):

sum_val = a + b

product_val = a * b

# 将一个包含两个值的元组返回

return (sum_val, product_val)

# 调用函数,并将返回值赋给一个变量

result = add_and_multiply(3, 4)

print(f"函数返回的结果是: {result}")

print(f"结果的类型是: {type(result)}")

# 我们可以通过解包来分别获取这两个值

sum_result, product_result = result

print(f"和是: {sum_result}, 积是: {product_result}")

# 输出:

# 函数返回的结果是: (7, 12)

# 结果的类型是:

# 和是: 7, 积是: 12

发生了什么?

add_and_multiply(3, 4) 被调用。函数内部计算出 sum_val 为 7,product_val 为 12。return (sum_val, product_val) 创建了一个元组对象 (7, 12),并将其引用返回。在外部,result = ... 这条语句将 result 这个标签指向了函数返回的那个元组对象。

如果没有 return 语句会怎样?

所有函数都有返回值。如果一个函数没有显式的 return 语句,它会自动在末尾执行 return None。

def just_print(name):

print(f"你好, {name}")

output = just_print("张三")

print(f"函数的返回值是: {output}")

print(f"返回值的类型是: {type(output)}")

# 输出:

# 你好, 张三

# 函数的返回值是: None

# 返回值的类型是:

总结

参数传递 (数据输入):通过赋值传递实现。函数内部会创建一个新变量名(标签),指向外部变量名所指向的同一个对象。

如果对象是不可变的(如数字、字符串),在函数内对其重新赋值,只会让内部变量指向一个新对象,不影响外部。如果对象是可变的(如列表、字典),在函数内通过方法修改对象内容,这个修改会反映到外部。

返回值传递 (数据输出):通过 return 关键字实现。它将一个值或对象的引用从函数内部“弹出”到外部,外部代码可以用一个变量来“接住”这个结果。如果函数没有 return,则默认返回 None。

理解这两个机制,可以帮助我们精确地控制数据在程序不同部分之间的流动,编写出更加清晰的代码。