趁热打铁,紧接上文,知识点梳理:
目录
一、函数在内存中调用问题:
从运行内存角度来分析我们的代码程序:
首先,一般本次语言都会把用到的内存是分为五个区域(栈区、堆区、全局区、静态区、代码区),而在本篇文章中我们用到的就有两个区(堆区、栈区)
堆(heap):内存中区域最大,对象都是放在堆中的,而函数在python中也是对象,所以函数就存储在堆内存中!
栈(stack):运行代码时就在栈内存中运行,因此它的内存空间不会太大;
每个程序开始运行时,都会分配一个执行栈让程序去执行
(当有多线程程序时,就会有多个执行栈);
基本数据类型是存储在栈里的。
它是前进后出(FILO)或者后进先出(LIFO)
所以函数调用的本质就是将函数临时压入执行栈!!!——即压栈(push)
a = 10
def test():
b = 230
print("hello")
test()
当程序开始执行运行时,就会往栈里压数据,因此可说整个执行栈就是整个程序的运行轨迹
当程序执行第一行代码,会将a压入栈;接着运行第二行代码,此时会有一个函数test()的引用,但这并不会自动执行,因为这一串并没有到栈里去,因为函数是要调用的,调用才会执行属于函数体的代码,因此当程序来到最后一行时开始进行函数调用!此时将函数压栈,当压栈操作完成后,在栈内存中便有了一块属于函数的内存因此在栈内存中的函数内存便执行函数体里的代码;当函数体里的代码一行一行的执行完,便会回到函数调用的那一行;至此,整个函数调用就结束了(这叫做出栈/弹栈pop)
函数在调用完成后,会退出内存,也意味着栈里不会有函数!而函数里的局部变量b就是在刚才释放的内存中存储的,因此出了函数体后访问不到局部变量b
so,函数在调用完成后,内存是会被回收的!!!
二、值传递与引用传递
在python中,向函数传递参数的类型有两种,一种是值传递,还有一种是引用传递。
1、值传递:
变量之间,数值的传递,这种传递相当于变量赋值;适用于实参类型为不可变类型(字符串、数值类型、元组、Boolean)
它的实质:当系统开始执行函数时,系统对形参执行初始化,就是把实参变量的值赋给函数的形参变量,在函数中操作的并不是实际的实参变量(在代码中,我们传入的是形参,在函数中修改形参是不会改变原先的值的,这是因为函数运行时候会先进行压栈,运行过程中会产线局部信息等,恰好,我们传入的形参就是该类型的值,所以运行后会出栈,出栈后函数所在的内存也会被销毁,所以函数内的局部变量随着出栈也被销毁了。所以直接修改形参无效)
这样讲会有点抽象,看代码容易理解:
在代码中,定义了一个变量x,并赋值为66,而后将x传入demo函数中,在函数中,将x赋值为99,打印一下函数中x的值;在主函数中在打印一下x的值;两者值不同
以上就是值传递
def demo(x):
x = 99
print("函数中修改过后的值:", x) # 99
x = 66
demo(x)
print("执行demo函数后的值:", x) # 66
2、引用传递:
传递的是地址(指针/对象)引用;适用于实参类型为可变类型(列表、字典、集合)
def demo(dict):
dict["x"] = 99
print("函数中修改过后的值:", dict) # 99
a = {"x": 66}
demo(a)
print("执行demo函数后的值:", a) # 99
定义一个字典a,其中有一个key为x,value(值)为66。在调用demo函数时,将a传递给了函数,在函数中,将字典a中key为x的赋值为99,打印一下函数中字典a的值;在主函数中在打印一下字典a的值;两者值相同
值传递与引用传递之间的区别:
def demo(obj):
obj += obj
print("形参值:", obj)
print("-----值传递-----")
a = 'name'
demo(a)
print("实参值:", a)
# 形参值: namename
# 实参值: name
print("-----引用传递-----")
a = [1, 2, 3]
demo(a)
print("实参值:", a)
# 形参值: [1, 2, 3, 1, 2, 3]
# 实参值: [1, 2, 3, 1, 2, 3]
#在执行值传递时,改变形式参数的值,实际参数并不会发生改变;而在进行引用传递时,改变形式参数的值,实际参数也会发生同样的改变。