欢迎访问Python每天3分钟系列。
每天花3分钟时间,学习或温习一个Python知识点。
今天是第038篇:
参数传递和内存结构
前面两篇讲了Python的mutable和imuutable对象,以及传值和传引用。好像大家问题挺多的。
现在我把我的电子书麦叔带你学Python中相关的章节直接复制过来,希望能够解决大家的疑问。
“完整电子书可以在麦叔编程公众号上回复book1获取。
当调用函数的时候,需要传递参数,这时候变量从一个函数传递到了另一个函数。
这个传递过程发生了?被传递的变量是被复制了一份呢?还是同一份呢?
可变数据类型和不可变数据类型
我们得先理解一下变量和对象的内存结构,尤其是list等集合类型的内存结构:
name = '麦叔'
print(id(name))
name = '张三'
print(id(name))
在内存里,它的过程是这样的:
-
变量和对象是两个概念,变量像一张可擦除的便签条,上面记录了对象的地址。对象是真正存放相关数据的地方。 -
上面的代码例子中:变量name有一个自己的空间(蓝色方块),存放它所代表的对象的地址(黄色方块)。 -
开始变量名 name 指向了“麦叔”;后来变量名 name指向了“张三”。 -
这时候“麦叔”不再有变量使用了,是孤魂对象,这种对象会被Python的垃圾回收器销毁掉。
对于一个复杂的数据类型,比如 list,原理是相同的,但是更复杂:
cities = ['北京', '上海', '广州', '深圳']
print(id(cities))
cities.append('雄安')
print(id(cities))
其中 name 等字符串是不可变的,当变量的值变化了,实际上生成了一个新的变量,内存地址变化了。基本数据类型都是不可变的,还有整数、小数等都是不可变。当一个变量的值发生了变化,实际上是创建了一个新的对象。
而上面代码中的cities 是个 list,是可变的,里面的值会发生变化,list的内存地址不会因为里面的值的变化而发生变化。
如果下图所示,当cities中增加了雄安以后,list地址并没有变化,只是它里面多记录了一个雄安。可以把list理解成一个便签本,里面可以记录很多对象的地址。
并不会因为list中的值的改变而创建新的list。所以它被称为可变对象(mutable)。下面列举了Python中的可变对象和不可变对象:
参数传递和引用次数
理解了变量的内存存储结构,我们再来看本文开始的的问题,参数传递到底是传递了什么?是复制了一份吗?
参数和返回值的传递都是传递的变量指向的内存地址:
import random
name = '麦叔'
print(f'name的地址:{id(name)}')
def shuai_score(person):
print(f'name的地址:{id(person)}')
score = random.randint(1, 10)
print(f'score的地址:{id(score)}')
return score
mscore = shuai_score(name)
print(f'mscore的地址:{id(mscore)}')
对于普通变量(不可变变量)和集合变量可能看起来效果不一样,但实际上原理是一样的:
-
全局变量 name 指向张三,这一直都没有变过; -
局部变量 name 本来是指向张三的,但是后来指向了李四,这并不影响全局变量还是指向张三。
name = 'zhangsan'
def hello(name):
print('传进来的是:'+ name)
name = 'lisi'
print('name被改成了' + name)
print(name)
注意,对于列表等可变对象,在函数中改变列表的值会影响全局变量的值,因为它们是同一个对象:
print(f'全局cities的地址:{id(cities)}')
def change_city(cities):
print(f'局部cities的地址:{id(cities)}')
cities[0] = '雄安'
print(f'局部cities的地址:{id(cities)}')
change_city(cities)
print(cities)
print(f'全局cities的地址:{id(cities)}')
评论(0)