卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

  • 函数、作用域与模块

    • Python函数参数是值传递还是引用传递?
      • 简要回答
      • 详细回答
      • 知识拓展
    • 位置参数、关键字参数、*args和**kwargs怎么用?
    • Python默认参数为什么不能随便使用列表和字典?
    • Python中的匿名函数、map、filter和reduce怎么用?
    • Python的LEGB作用域是什么?
    • global和nonlocal有什么区别?
    • 什么是闭包?闭包中的变量为什么会出现延迟绑定?
    • Python模块和包是什么?import是怎么工作的?
  • 面向对象

  • 进阶语法与语言机制

# Python函数参数是值传递还是引用传递?

面试官问:"Python函数传参是值传递还是引用传递?"

这题最容易答成"Python一切都是引用传递",但这个说法并不准确。真正要讲清楚的是:函数拿到的是同一个对象的引用副本,参数重新绑定和修改可变对象不是一回事。

# 简要回答

Python采用的是对象共享传递(call by object reference / call by sharing),不是传统意义上的值传递或引用传递。

调用函数时,形参会绑定到实参指向的同一个对象。这带来两个行为:

重新绑定形参不会影响外部变量。函数内执行param = new_value,只是让局部名字param指向另一个对象,调用方的变量仍然指向原来的对象。

通过形参修改可变对象,会影响外部看到的同一个对象。函数内执行items.append(x),修改的是调用方和函数共享的那个列表对象,外部自然也能看到变化。

# 详细回答

# 函数调用时发生了什么

Python中,变量是名字,对象独立存在。调用函数时:

  1. 实参表达式求值,得到对象。
  2. 形参名字绑定到这个对象。
  3. 函数内通过形参名访问对象。
  4. 函数返回后,形参名字消失,但对象是否被修改取决于函数内的操作。

形参与实参绑定同一个对象,不是复制对象内容,也不是传递变量本身的地址。

函数参数对象绑定

# 重新绑定不会修改外部对象

def reset_list(items):
    items = []  # 重新绑定局部名字items

courses = ["Python", "Go"]
reset_list(courses)
print(courses)  # ['Python', 'Go']
1
2
3
4
5
6

函数内items = []创建了一个新列表,让局部名字items指向它。外部的courses仍然指向原来的列表,所以courses的内容没有变化。

# 修改可变对象会影响外部

def append_item(items):
    items.append("C++")  # 原地修改对象

courses = ["Python", "Go"]
append_item(courses)
print(courses)  # ['Python', 'Go', 'C++']
1
2
3
4
5
6

items.append("C++")调用列表对象的方法,原地修改了这个列表。因为items和courses共享同一个对象,外部看到的列表自然也被修改了。

# 不可变对象的行为

对于不可变对象(整数、字符串、元组),函数内尝试修改时,Python实际上创建了新对象:

def increment(num):
    num = num + 1  # 创建新整数对象并重新绑定
    return num

count = 10
result = increment(count)
print(count)   # 10,外部变量未变
print(result)  # 11
1
2
3
4
5
6
7
8

num = num + 1计算后生成新整数对象11,num绑定到新对象,count仍然指向10。

# 常见错误:以为传的是变量本身

def swap(a, b):
    a, b = b, a  # 只是交换局部名字的绑定

x, y = 1, 2
swap(x, y)
print(x, y)  # 1 2,外部变量顺序未变
1
2
3
4
5
6

a, b = b, a只是让函数内的局部名字a和b交换了指向,不会影响外部的x和y。

# 知识拓展

Q1:为什么不说"引用传递"?

传统的引用传递(如C++中的int &x)允许函数直接修改外部变量的绑定。Python函数内重新绑定形参不会影响外部变量,所以不是引用传递。更准确的术语是对象共享传递或传对象引用。

Q2:如何让函数修改外部的不可变对象?

不可变对象本身不能被修改,可以把它包在可变容器里:

def increment_wrapped(container):
    container[0] = container[0] + 1

num = [10]
increment_wrapped(num)
print(num[0])  # 11
1
2
3
4
5
6

或者直接返回新值:

def increment(num):
    return num + 1

count = increment(count)
1
2
3
4

Q3:id()能看出对象是否相同吗?

可以。id(obj)返回对象的身份标识(在CPython中是内存地址)。形参和实参的id()相同,说明它们指向同一个对象。

Q4:函数参数会复制对象内容吗?

不会。无论对象多大,传参只是复制引用(一个指针),不复制对象本身。这也是为什么传递大列表或大字典不会有性能问题。

Q5:如何避免函数意外修改可变参数?

函数内先复制一份:

def process_items(items):
    items = items.copy()  # 浅拷贝,不影响外部
    items.append("new")
    return items
1
2
3
4

或者明确在调用时传副本:

result = process_items(courses.copy())
1
Last Updated: 10/13/2026, 5:57:00 PM

← 列表、字典和集合推导式怎么写? 位置参数、关键字参数、*args和**kwargs怎么用? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪