关于 python 参数传递的理解
之前和同学聊到了 python 关键字 del 与内存回收相关的问题。通过以下内容可以帮助理解
参数
参数有形参和实参之分。形参也就是形式参数,不在内存中占用内存地址,如 def 定义函数时括号内的变量就是形参。实参全称为实际参数,在调用函数时提供的值或者变量称作为实际参数,占用内存地址。
#下面 x,y 即为形参
def add(x,y):
return x+y
#1,2 为实参
add(1,2)
#以下的 x,y 为实参
x=2
y=3
add(x,y)
参数的传递
首先我们来看一段 python 代码
if __name__=='__main__':
a=2
b=a
c=a
del a
del b
print(c)
最终程序得到的结果为 2。可能有人会感觉很奇怪,明明 a 已经被 del 了,c=a 还是可以得到 1 的结果。
所以我们需要明确概念,del 作为 python 的关键字,不同于 C free 和 C++ delete。del 语句只作用于变量,而不作用于数据对象。所以 del a 只是删除了变量 a,而不是数据对象。而 python 有 GC 机制也就是垃圾回收机制,将 a 和 b del 之后,2 的引用计数仍然为 1,所以不会被清除。只有当引用次数为 0 后,数据对象内存地址被自动回收。
什么叫引用
在 python 中,一切皆对象。python 中赋值语句 a=2 可以把 a 看成是对象 2 的一个引用。
文章的下面内容,我们将详细分析
python 的 GC 机制是什么
GC 机制也就是垃圾回收机制。当对象 2 的引用计数为 0 时,将自动被清除。
具体分析如下:
if __name__=='__main__':
a=2 #2 的引用次数=1
b=a #2 的引用次数+1,此时为 2
c=a #2 的引用次数+1,此时为 3
del a #引用次数-1,此时为 2
del b #引用次数-1,此时为 1
print(c) #c 的值为 2
参数的传递和改变
首先我们学习一个函数 id(),它是 python 自带的函数,函数的解释是返回对象的唯一标识,CPython 使用的对象的内存地址。其实总结就是返回对象的内存地址。
>>> x=8
>>> id(x)
1617486720
>>> id(2)
1617486720
>>> y='hi'
>>> id(y)
35985280
>>> id('hi')
35985280
从以上代码可以看出,id(x) 和 id(8) 的值是一样的,id(y) 和 id(‘hi’) 的值也是一样的。
在 Python 中一切皆对象。8,’hi’都是对象,8 是一个整型对象,而’hi’是一个字符串对象。上面的 x=8,在 python 中实际的处理过程是这样的:先申请一段内存分配给一个整型对象来存储整型值 8,然后让变量 x 去指向这个对象,实际上就是指向这段内存(类似 C 语言中的指针)。
而 id(8) 和 id(x) 的结果一样,说明 id 函数在作用于变量时,其返回的是变量指向的对象的地址。因为变量也是对象,所以在这里可以将 x 看成是对象 8 的一个引用。
下面我们在看几个例子。
例一:
a=6
b=6
print(id(a))
print(id(b))
print(id(6))
s='hello'
t=s
print(id('hello'))
print(id(s))
print(id(t))
通过执行以上代码可以发现,id(a)、id(b)、id(6) 的结果相同。id(‘hello’),id(s) 和 id(t) 的结果也是相同的。说明 a 和 b 指向的是同一对象,而 t 和 s 也是指向的同一对象。a=6 这句让变量 a 指向了 int 类型的对象 6,而 a=6 这句执行时,并不重新为 6 分配地址空间,而是让 a 直接指向了已经存在的 int 类型的对象 2。
这个很好理解,因为本身只是想给 a 赋一个值 2,而在内存中已经存在了这样一个 int 类型对象 2,所以就直接让 y 指向了已经存在的对象。这样一来不仅能达到目的,还能节约内存空间。t=s 这句变量互相赋值,也相当于是让 t 指向了已经存在的字符串类型的对象’hello’。
例二:
x=1
y=2
print(id(1),id(x))
x=3
print(id(3),id(x))
执行以上代码后,前后两次执行 id(x) 得到的值不同。x=1 时,申请一段内存存储对象 1,再将变量 x 只想对象 1。执行 x=3 时,同样是申请一段内存存储对象 3,再将变量 x 重新指向对象 3,此时对象 1 的引用次数变为 0。并不是获取 x 指向对象 1 的内存地址,再将内存中的值改为 1.
例三:
L=[1,2,3]
x=3
M=L
print(id(M),id(L))
L[0]=3
print(M,L)
print(id(M),id(L))
print(id(x),id(L[0]),id(L[2]))
执行结果如下
2269912 2269912
[3, 2, 3] [3, 2, 3]
2269912 2269912
1617486640 1617486640 1617486640
在 Python 中,复杂元素的对象是允许更改的,比如列表、字典、元组等。Python 中变量存储的是对象的引用,对于列表,其 id() 值返回的是列表第一个子元素 L[0]本身的存储地址。上面的例子,L=[1,2,3],这里的 L 有三个子元素 L[0],L[1],L[2]。L[0]、L[1]、L[2]分别指向对象 1、2、3。M 和 L 是指向同一个对象,所以在 L 中子元素的值后,M 中子元素的值也相应改变了。但是 id(L) 和 id(M) 的值没有改变,因为 L[0]=3 只是让 L[0]指向了对象 3,并没有改变 L[0]自身的存储地址。(id(L) 的值实际等于 L[0]自身的存储地址)
分析上面代码的执行结果,可以得出与上面分析一样的结论。
画一个简单的示意图帮助我们进行理解
L —–> | L[0] | —–> 1
| L[1] | —–> 2
| L[2] | —–> 3 <—– x
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: centos 常用相关命令总结
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论