Python中参数是引用传递,还是值传递

本文探讨了Python中引用传递和值传递的区别,通过具体案例分析了如何避免因引用传递而导致的数据意外修改,并解释了变量与对象的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目前在使用python做开发,有一个多月了,但是对python的引用传递和值传递,理解的不是很清楚,前几天做一个单子,其中将一个参数作为初始化值传递给一个接口,结果出了bug。问题主要是这样的,客户端做一个界面,界面上面有三个框,主要是将候选字拖到里面,那么在服务端每个人都对应这样一个框,也就是需要保存这个三个框的状态,需要持久化数据。调用的接口的定义如下:

class MyCounter(object):
	def __init__(self, id):
		self.m_Data = {}
	
	#iSecond是数据持久化时间
	def Set(self,key, val, iSecond):
		self.Detele(key)
		data.m_Data[key] = (val, iSecond)
	
	def Query(self, key, val, default = 0):
		data = self.m_Data
		if data.has_key(key):
			return data[key][0]
		return default

	def Delete(self, key):
		if self.m_Data.has_key(key):
			del self.m_Data[key]		

客户端打开界面的时候,我会发送该框的数据给他,这个时候调用了上面的接口

INIT_WORDS_TBLE = {1:0, 2:0, 3:0}
tempDict = user.counter.QUery("%s.wordsState" % self.hdName(), INIT_WORDS_TBL)
#发送tempDict中的数据到客户端

客户端拖数据到框(激活候选字)中,然后服务端,需要更新人身上的框数据wordsState

#i表示框的编号,j表示哪个字
def C2SActive(self, i, j):
	tempDict = user.counter.QUery("%s.wordsState" % self.hdName(), INIT_WORDS_TBL)
	tempDict[i] = j

问题就是出在这里,这个地方tempDIct是引用,这里如果修改那么,INIT_WORDS_TBL将被修改,所以这里可以如下修改:

tempDict = dict(tempDict)

为什么会这样:

变量与对象

Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。例如,[]是一个空列表对象,变量 a 是该对象的一个引用
a = []
a.append(1)
在 Python 中,「变量」更准确叫法是「名字」,赋值操作 = 就是把一个名字绑定到一个对象上。就像给对象添加一个标签。
a = 1
这里写图片描述
整数 1 赋值给变量 a 就相当于是在整数1上绑定了一个 a 标签。
a = 2
这里写图片描述
整数 2 赋值给变量 a,相当于把原来整数 1 身上的 a 标签撕掉,贴到整数 2 身上。
b = a
这里写图片描述

有了解释,则可以知道
tempDict[i] = j
中的tempDict它是引用,他的地址跟之前传入的INIT_WORDS_TBL的地址是一致的,如果对他整体赋值,它的地址就会改变,而之前的地址仍然是之前的地址,两者之间的关联断了。这里还需要说明的是,元组的元素不可以更新,但是元组中的可变的元素可以更新,这里的元素是字典,属于可变。

<think>我们刚刚讨论过Python函数的基本用法,现在用户提出了一个关于参数传递机制的具体问题:Python函数参数传递值传递还是引用传递?这个问题在Python中需要深入理解,因为Python参数传递机制既不是纯粹的值传递,也不是纯粹的引用传递,而是所谓的“共享传递”(Callbysharing)。在Python中,所有的数据都是对象,变量实际上是对对象的引用。当我们将一个变量传递给函数时,实际上传递的是这个引用的拷贝(即值传递方式传递引用)。这意味着:-如果参数是不可变对象(如整数、字符串、元组等),函数内部对参数的修改不会影响原始对象,因为不可变对象本身不能被修改。-如果参数是可变对象(如列表、字典等),函数内部对参数的修改会影响到原始对象,因为函数内部和外部使用的是同一个对象。下面我们用代码示例来说明:</think> ### Python函数参数传递机制解析 Python参数传递既不是纯粹的值传递(pass by value),也不是纯粹的引用传递(pass by reference),而是采用 **对象引用传递(pass by object reference)** 机制。以下是关键原理和验证代码: #### 核心机制: 1. **传递对象引用**:函数接收的是实参指向对象的**内存地址的副本** 2. **可变与不可变对象差异**: - 对**不可变对象**(int, str, tuple等):函数内修改会创建新对象 - 对**可变对象**(list, dict等):函数内修改直接影响原对象 #### 验证代码: ```python def modify_data(immutable_arg, mutable_arg): print("\n函数内初始状态:") print(f"immutable_arg 地址: {id(immutable_arg)}") print(f"mutable_arg 地址: {id(mutable_arg)}") # 修改不可变对象 immutable_arg += 100 # 修改可变对象 mutable_arg.append(4) print("\n函数内修改后:") print(f"immutable_arg 地址: {id(immutable_arg)}") print(f"mutable_arg 地址: {id(mutable_arg)}") # 测试数据 num = 10 # 不可变对象 my_list = [1,2,3] # 可变对象 print("调用前原始数据:") print(f"num 地址: {id(num)} | : {num}") print(f"my_list 地址: {id(my_list)} | : {my_list}") modify_data(num, my_list) print("\n调用后原始数据:") print(f"num : {num}") # 保持原 print(f"my_list : {my_list}") # 已被修改 ``` #### 执行结果分析: ``` 调用前原始数据: num 地址: 140726300298208 | : 10 my_list 地址: 2048302561408 | : [1, 2, 3] 函数内初始状态: immutable_arg 地址: 140726300298208 # 与外部num相同地址 mutable_arg 地址: 2048302561408 # 与外部my_list相同地址 函数内修改后: immutable_arg 地址: 140726300299488 # 新地址(创建了新int对象) mutable_arg 地址: 2048302561408 # 地址不变(修改原对象) 调用后原始数据: num : 10 # 未改变 my_list : [1, 2, 3, 4] # 已改变 ``` #### 关键结论: 1. **对象引用副本**传递:函数参数获得原始引用的副本 2. **重新赋 vs 就地修改**: - `immutable_arg += 100` → 创建新对象并绑定到局部变量 - `mutable_arg.append(4)` → 直接修改共享对象 3. **内存地址不变性**:可变对象在函数内外的内存地址相同 4. **副作用限制**:不可变对象在函数内修改不会影响外部变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值