Python函数传参机制:可变对象与引用传递详解
本文深入解析Python函数传参时可变对象(如dict、list、set)的引用传递机制。通过代码示例说明修改源对象如何影响目标对象,并指出常量字面量与变量引用的区别。结合配置管理类实例,探讨利用共享可变对象优化内存与性能的场景,同时提供避免意外副作用的最佳实践建议。
在Python中传递参数时,若传入的是可变对象,实际上传递的是指向该对象内存地址的引用(或称为指针)。
这一结论源于实际项目中的观察。对于熟悉C语言的开发者而言,函数传参既支持值传递,也支持指针传递,其优势在此不再赘述。
首先通过代码示例观察具体表现:
def trans(var):
return var
source = {1: 1}
dist = trans(source)
source[2] = 2
print(source)
print(dist)
运行结果如下:
{1: 1, 2:2} {1: 1, 2:2}
从结果可见,当修改source时,dist也随之改变。这是因为source为可变对象,传参时传递的是其引用,导致dist和source指向同一块内存空间。执行source[2] = 2时,直接修改了该内存空间的数据,因此dist的显示内容也同步更新。
这种机制有何实际用途?应用场景众多,鉴于个人经验有限,此处仅以自身项目为例进行说明。
项目中定义了一个类,用于读写配置并预存部分JSON配置数据。客户端读取配置时,若预存数据中不包含所需配置,则从设备端获取。
需要实例化多个该类对象以对应多个客户端,但希望预存的配置数据是公共的。这样,当陌生配置被请求时,无需所有客户端都单独从设备读取,从而提升效率。
最初的实现方式如下:
global dataset
dataset = {}
class Config(object):
def __init__(self, device_url):
self.device_url = device_url
def get_config(self, key):
global dataset
if key in dataset:
return dataset.get(key)
else:
# 通过device_url从设备获取配置,假如赋值给了value
dataset[key] = value
return value
def other_func(self):
# 其他函数,跟device_url有关
pass
随后,需求扩展为需要多份公共配置,甚至超过1000份。显然,使用全局变量已无法满足需求。为了实现内存共享,我将代码修改为传递可变对象的形式:
class Config(object):
def __init__(self, dataset, device_url): # 传递可变对象dataset
self.dataset = dataset
self.device_url = device_url
def get_config(self, key):
if key in self.dataset:
return self.dataset.get(key)
else:
# 通过device_url从设备获取配置,假如赋值给了value
self.dataset[key] = value # 可变对象dataset赋值,其他实例化的dataset属性值也会变化
return value
def other_func(self):
# 其他函数,跟device_url有关
pass
需要注意的是,列表、字典、集合并不总是可变对象。
网上许多资料声称列表、字典、集合是可变对象,这种说法并不完全准确。字面量形式的{}、[]、set((, ))等常量本身是不可变对象。
在上述Config类中,如果实例化时直接传递{},则无法实现配置的共享。
config1 = Config({})
config2 = Config({})
config1.dataset[1] = 1
print(repr(config1.dataset))
print(repr(config2,dataset))
上述代码的运行结果为:
'{1: 1}' 'None'
但如果采用以下方式:
share_var = {}
config1 = Config(share_var)
config2 = Config(share_var)
config1.dataset[1] = 1
print(repr(config1.dataset))
print(repr(config2,dataset))
运行结果将变为:
'{1: 1}' '{1: 1}'
此处share_var是可变对象,而{}是不可变对象,尽管两者的值相同,但传递行为不同。
要深入理解这一现象,需要掌握Python的命名空间机制。
传参及传递可变对象参数时的注意事项。
- 如果并非有意通过引用/指针操作对应内存空间,传参时应避免直接传递字典、列表、集合、类或类的实例化对象等类型。
- 传递可变对象参数时,务必避免直接传递常量{}、[]、set((, )),最佳做法是在传参前将其赋值给一个变量,然后传递该变量。
理解原理后,虽然可能避免直接传常量,但仍可能出现以下情况:
def func1(mutable_object, flag):
if flag:
return mutable_object
else:
return {}
def func2(mutable_object):
# something to do with mutable_object
pass
func2(func1(mutable_object, False)) # 此处func1(mutable_object, False)返回的是{},是一个不可变对象
至此,关于Python函数传参传递dict/list/set等类型的问题已介绍完毕。更多相关Python函数传参内容,请搜索本站历史文章或继续浏览相关文章,希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













