关于Python函数传参是传值还是传引用?这一问题网上都有很多的讨论,这篇博客解释比较清楚,结论是:Python参数传递采用的是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符串或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。
那么现在新的问题来了,对于函数的返回值来说,返回的是“值”还是“引用”呢?
这里先说下结论,与函数传参一样,返回的也是“对象引用”,如果返回的对象是可变对象,则将函数的返回值赋值给新变量之后,对新变量的修改会直接影响到源对象。
例如如下的代码:
original = {
'data': 'hi'
}
def return_original():
return original
new = return_original()
print(id(original), id(new)) #4437939256 4437939256。地址一样
new['data'] = 'hello' #使用新变量修改字典中字段的值
print(original) #{'data': 'hello'}。原来的对象也被修改了
方法return_original
返回的是全局变量original
,此变量的值是一个字典。运行return_original
方法,并将返回值赋值给新的变量new
,通过打印original
和new
的内存地址可以发现他们指向的是同一块区域。然后使用new
变量修改字段中data
字段的值,最后打印original
发现其值也已同步更新了。
如果将original
改为字符串这种不可变对象会发生什么情况呢?
original = 'hi'
def return_original():
return original
new = return_original()
print(id(original), id(new)) #4404165016 4404165016。内存地址还是一样
new = 'hello' # 修改新变量的值
print(original) # hi。原来变量的值没有被更改,因为字符串是不可变对象
print(id(original), id(new)) #4404165016 4403912240。original内存地址没变,new变量的内存地址变了
通过实验发现。此结论应用到类上面也一样。假如类中存在一个方法a
,其返回值是一个实例属性或类属性。在类中另一个方法b
中调用a
并将其返回值赋值给新变量new
,如果返回的实例属性是一个可变对象,那么针对new
变量的更改也会直接反应在原始的实例属性或类属性上。
下面的代码演示了这一结论:
class Test:
def __init__(self):
self.class_obj = {
'data': 'hi'
}
def a(self):
return self.class_obj
def b(self):
new = self.a()
print(id(new), id(self.class_obj), self.class_obj) #4408660976 4408660976 {'data': 'hi'}
new['data'] = 'hello'
print(id(new), id(self.class_obj), self.class_obj) #4408660976 4408660976 {'data': 'hello'}
test_obj = Test()
test_obj.b()
完!
参考资料:
python函数传参是传值还是传引用?
网友评论