欢迎光临
我们一直在努力

Python篇---python参数传递

Python函数变量传递:像快递一样传递数据

一、一句话理解

Python函数传递变量就像送快递:

  • 传递值 = 送物品的复印件(原物不变)

  • 传递引用 = 送仓库的地址(仓库里的东西可能变)


二、先看两个重要概念

1. 可变类型 vs 不可变类型

# 不可变类型(immutable) – 内容不能改
# 如果想改,只能创建新的
name = "小明" # 字符串
age = 18 # 整数
height = 175.5 # 浮点数
is_student = True # 布尔值
colors = (1, 2, 3) # 元组

# 可变类型(mutable) – 内容可以改
scores = [85, 90, 78] # 列表
info = {"name": "小红"} # 字典
friends = {"小明", "小红"} # 集合

比喻:

  • 不可变 = 写在石头上的字(不能改,只能换石头)

  • 可变 = 写在黑板上的字(可以擦掉重写)


三、函数参数传递的两种方式

方式1:传递不可变类型(送复印件)

def change_number(x):
"""尝试改变数字"""
print(f"函数内修改前: x = {x}")
x = 100 # 修改x的值
print(f"函数内修改后: x = {x}")

# 主程序
num = 10
print(f"调用函数前: num = {num}")

change_number(num) # 把num传给函数

print(f"调用函数后: num = {num}") # num还是10!

输出结果:

调用函数前: num = 10
函数内修改前: x = 10
函数内修改后: x = 100
调用函数后: num = 10 # 没变!

快递比喻:

你(主程序)写了一张纸条:num = 10
你把纸条复印一份给快递员(函数)
快递员在复印件上改成:x = 100
你手上的原件还是:num = 10
复印件改了,原件不变!


方式2:传递可变类型(送仓库地址)

def change_list(my_list):
"""尝试改变列表"""
print(f"函数内修改前: my_list = {my_list}")
my_list.append(100) # 修改列表内容
print(f"函数内修改后: my_list = {my_list}")

# 主程序
scores = [85, 90, 78]
print(f"调用函数前: scores = {scores}")

change_list(scores) # 把scores传给函数

print(f"调用函数后: scores = {scores}") # scores变了!

输出结果:

调用函数前: scores = [85, 90, 78]
函数内修改前: my_list = [85, 90, 78]
函数内修改后: my_list = [85, 90, 78, 100]
调用函数后: scores = [85, 90, 78, 100] # 变了!

仓库比喻:

你(主程序)有一个仓库,里面放着[85, 90, 78]
你把仓库地址告诉快递员(函数)
快递员去这个仓库,放进了100
你看仓库时,发现变成了[85, 90, 78, 100]
仓库地址没变,但仓库里的东西变了!


四、可视化理解:变量是"标签"还是"盒子"?

Python变量的本质:标签系统

# 传统观念(错误):变量是盒子
# Python实际(正确):变量是标签

# 情况1:创建新变量
a = [1, 2, 3] # 创建一个列表,贴上标签"a"
b = a # 给同一个列表再贴一个标签"b"
print(f"a: {a}") # [1, 2, 3]
print(f"b: {b}") # [1, 2, 3]

# 修改列表
a.append(4) # 通过标签a修改列表
print(f"修改后 a: {a}") # [1, 2, 3, 4]
print(f"修改后 b: {b}") # [1, 2, 3, 4] # b也变了!

# 情况2:重新赋值
a = [5, 6, 7] # 创建一个新列表,把标签a贴上去
print(f"重新赋值后 a: {a}") # [5, 6, 7]
print(f"重新赋值后 b: {b}") # [1, 2, 3, 4] # b没变!

图解:

初始:
标签a → [1, 2, 3]
标签b → [1, 2, 3] # 两个标签贴在同一张纸上

a.append(4)后:
标签a → [1, 2, 3, 4]
标签b → [1, 2, 3, 4] # 还是同一张纸

a = [5, 6, 7]后:
标签a → [5, 6, 7] # 贴到新纸上
标签b → [1, 2, 3, 4] # 还在原来的纸上


五、函数参数传递的完整例子

例子1:传递不可变类型

def process_data(data):
print(f"函数内收到: {data}, id={id(data)}")
data = data.upper() # 创建新的字符串
print(f"函数内修改: {data}, id={id(data)}")
return data

# 测试
text = "hello"
print(f"函数前: {text}, id={id(text)}")

result = process_data(text)
print(f"函数返回: {result}, id={id(result)}")
print(f"原始text: {text}, id={id(text)}") # 没变!

输出:

函数前: hello, id=1400000000 # 地址A
函数内收到: hello, id=1400000000 # 相同地址A
函数内修改: HELLO, id=1400000001 # 新地址B
函数返回: HELLO, id=1400000001 # 返回新地址B
原始text: hello, id=1400000000 # 还是地址A

理解: 字符串不可变,只能创建新的


例子2:传递可变类型

def update_list(items):
print(f"函数内收到: {items}, id={id(items)}")
items.append("new") # 修改原列表
items[0] = "changed" # 修改原列表
print(f"函数内修改: {items}, id={id(items)}")

# 测试
my_list = ["apple", "banana"]
print(f"函数前: {my_list}, id={id(my_list)}")

update_list(my_list)
print(f"函数后: {my_list}, id={id(my_list)}") # 变了!

输出:

函数前: ['apple', 'banana'], id=1400000000 # 地址A
函数内收到: ['apple', 'banana'], id=1400000000 # 相同地址A
函数内修改: ['changed', 'banana', 'new'], id=1400000000 # 相同地址A
函数后: ['changed', 'banana', 'new'], id=1400000000 # 相同地址A,内容变了

理解: 列表可变,可以直接修改


例子3:特殊的"重新赋值"

def replace_list(items):
print(f"函数内收到: {items}, id={id(items)}")
items = ["new", "list"] # 重新赋值,创建新的!
print(f"函数内赋值: {items}, id={id(items)}")

# 测试
my_list = ["apple", "banana"]
print(f"函数前: {my_list}, id={id(my_list)}")

replace_list(my_list)
print(f"函数后: {my_list}, id={id(my_list)}") # 没变!

输出:

函数前: ['apple', 'banana'], id=1400000000 # 地址A
函数内收到: ['apple', 'banana'], id=1400000000 # 相同地址A
函数内赋值: ['new', 'list'], id=1400000001 # 新地址B
函数后: ['apple', 'banana'], id=1400000000 # 还是地址A

关键区别:

  • items.append() = 修改原列表(地址不变)

  • items = […] = 创建新列表(新地址)


六、参数的四种传递方式

1. 位置参数(按顺序传递)

def greet(name, greeting):
return f"{greeting}, {name}!"

# 按位置传递
print(greet("小明", "你好")) # 你好, 小明!
print(greet("你好", "小明")) # 小明, 你好! # 顺序错了!

2. 关键字参数(按名字传递)

def greet(name, greeting):
return f"{greeting}, {name}!"

# 按关键字传递(顺序不重要)
print(greet(name="小明", greeting="你好")) # 你好, 小明!
print(greet(greeting="你好", name="小明")) # 你好, 小明! # 顺序对了!

3. 默认参数(不传就用默认值)

def greet(name, greeting="Hello"): # greeting有默认值
return f"{greeting}, {name}!"

print(greet("小明")) # Hello, 小明!
print(greet("小红", "Hi")) # Hi, 小红!

4. 可变参数(传任意数量)

# *args: 接收任意数量的位置参数
def sum_all(*args):
total = 0
for num in args:
total += num
return total

print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15

# **kwargs: 接收任意数量的关键字参数
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")

print_info(name="小明", age=18, city="北京")
# name: 小明
# age: 18
# city: 北京


七、函数返回值:怎么"拿回来"数据?

返回单个值

def add(a, b):
result = a + b
return result # 返回计算结果

# 接收返回值
sum_result = add(10, 20)
print(f"10 + 20 = {sum_result}") # 30

返回多个值(实际是返回元组)

def calculate(a, b):
add_result = a + b
multiply_result = a * b
return add_result, multiply_result # 返回两个值

# 接收多个返回值
add_value, multiply_value = calculate(5, 3)
print(f"加法: {add_value}") # 8
print(f"乘法: {multiply_value}") # 15

# 实际上返回的是元组
result = calculate(5, 3)
print(f"返回类型: {type(result)}") # <class 'tuple'>
print(f"返回内容: {result}") # (8, 15)


八、引用传递的常见陷阱

陷阱1:默认参数是可变对象

# ❌ 错误做法
def add_item(item, items=[]): # 默认参数是列表
items.append(item)
return items

print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['apple', 'banana'] # 咦?怎么有apple?

# ✅ 正确做法
def add_item_correct(item, items=None):
if items is None: # 每次都创建新列表
items = []
items.append(item)
return items

print(add_item_correct("apple")) # ['apple']
print(add_item_correct("banana")) # ['banana'] # 正确!

原因: 默认参数在函数定义时就创建了,每次调用都使用同一个列表!


陷阱2:以为创建了副本

# ❌ 错误:以为创建了独立副本
original = [[1, 2], [3, 4]]
shallow_copy = original.copy() # 浅拷贝

shallow_copy[0][0] = 99 # 修改嵌套列表
print(f"浅拷贝: {shallow_copy}") # [[99, 2], [3, 4]]
print(f"原始列表: {original}") # [[99, 2], [3, 4]] # 原列表也变了!

# ✅ 正确:使用深拷贝
import copy
deep_copy = copy.deepcopy(original) # 深拷贝
deep_copy[0][0] = 100
print(f"深拷贝: {deep_copy}") # [[100, 2], [3, 4]]
print(f"原始列表: {original}") # [[99, 2], [3, 4]] # 原列表不变


九、变量作用域:数据在哪里有效?

局部变量 vs 全局变量

# 全局变量(在整个文件有效)
global_score = 100

def test_scope():
# 局部变量(只在函数内有效)
local_score = 50
print(f"函数内局部变量: {local_score}")

# 可以读取全局变量
print(f"函数内读取全局变量: {global_score}")

# 如果想修改全局变量
global global_score # 声明要修改全局变量
global_score = 200 # 修改全局变量

test_scope()
print(f"函数外全局变量: {global_score}") # 200
# print(local_score) # 错误!local_score不存在

作用域规则:

  • 局部优先:先找局部变量

  • 能读全局:可以读取全局变量

  • 修改需声明:修改全局变量要用global


  • 十、实战练习

    练习1:预测输出

    def mystery(x, y):
    x = x + 10
    y.append(99)
    return x, y

    a = 5
    b = [1, 2, 3]
    result = mystery(a, b)

    print(f"a = {a}")
    print(f"b = {b}")
    print(f"result = {result}")

    练习2:修复bug

    # 这个函数有问题,你能修复吗?
    def update_scores(scores, new_score):
    # 应该添加新分数,但不改变原列表
    scores.append(new_score)
    return scores

    original = [85, 90]
    updated = update_scores(original, 95)
    print(f"原始: {original}") # 应该是[85, 90]
    print(f"更新: {updated}") # 应该是[85, 90, 95]


    十一、简单记忆口诀

    Python传参有两种,理解清楚不糊涂。
    不可变类型传复印件,函数修改不影响外。
    可变类型传仓库址,里面东西可能变。

    变量本质是标签,不是盒子装东西。
    多个标签贴一处,一处改动处处知。
    重新赋值换标签,原物还在不消失。

    参数传递方式多,位置关键字默认值。
    *args接收任意量,**kwargs关键字集。
    返回值用return,多个值其实是元组。

    作用域要分清楚,局部全局不同路。
    全局变量可读取,修改需要用global。
    默认参数要小心,可变对象是陷阱。

    传参就像送快递,复印件地址要分清。
    仓库比喻记心间,写代码时不迷惑。

    十二、最后的重要总结

    关键点回顾:

  • 不可变类型(数字、字符串、元组):传复印件,函数内修改不影响外面

  • 可变类型(列表、字典、集合):传地址,函数内修改会影响外面

  • 变量是标签,不是盒子

  • = 是贴新标签,.append() 是修改内容

  • 实用技巧:

    • 不想改变原列表?用 copy() 或 list(original)

    • 不想改变嵌套结构?用 copy.deepcopy()

    • 默认参数避免用可变对象,用 None 代替

    记住这个终极比喻:

    你写日记(数据):
    – 如果是石头上(不可变):只能拓印给朋友看,原石不变
    – 如果是本子上(可变):可以把本子给朋友,朋友也能写

    Python传参:
    – 传不可变 = 给朋友看复印件
    – 传可变 = 把本子借给朋友

    现在你对Python函数传参应该很清楚了!去写代码实践一下吧!🚀

    赞(0)
    未经允许不得转载:171主机测评 » Python篇---python参数传递
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址