从内存管理的角度来看,Python语言的变量和参数传递情况解析
概述
从内存管理的角度来看,Python中的变量和参数传递有一些特点:
☆ 变量是对象的引用:在Python中,变量实际上是对象的引用,而不是对象本身。当你给一个变量赋值时,实际上是将变量指向了一个对象。这意味着变量可以指向不同类型的对象,并且可以在程序中随时改变指向的对象。
☆ 引用计数:Python使用引用计数来管理内存。每个对象都有一个引用计数,表示有多少个变量引用了该对象。当引用计数为0时,对象将被自动回收。当一个变量不再引用一个对象时,引用计数会减少。当引用计数为0时,对象的内存将被释放。
☆ 对象的可变性:Python中的对象分为可变对象和不可变对象。可变对象(如列表、字典)的值可以被修改,而不可变对象(如整数、字符串、元组)的值不能被修改。这意味着如果你修改了一个可变对象,那么所有引用这个对象的变量都会受到影响。
☆ 参数传递方式:在Python中,函数的参数传递是按值调用(call by value) 来传递的(其中的 值 始终是对象的 引用 而不是对象的值)——实际上,按对象引用调用(call by object reference)调用这种说法更好。对于不可变对象(如整数、字符串、元组),由于它们的值不能被改变,所以函数内部对这些对象的修改实际上是创建了一个新的对象。因此,函数内部的修改不会影响到函数外部的实际参数。对于可变对象(如列表、字典),由于它们的值可以被改变,所以函数内部对这些对象的修改会直接改变原始对象的值。因此,函数内部的修改会影响到函数外部的实际参数。
在Python中,id()函数用于获取对象的唯一标识符(即内存地址)。每个对象(Python语言中,所有的 数据 都被称之为 对象)在内存中都有一个唯一的标识符,可以通过id()函数来获取。
例如id(1500),它会返回整数1500的唯一标识符(内存地址)。这个标识符是一个整数值,用于唯一标识该对象在内存中的位置,类似如1878476699696,注意,这个值在不同的运行时环境中可能会有所不同,因为它取决于内存管理和对象分配的具体实现。Python的内存管理和对象分配模型是自动的,由Python的内存管理器负责。
请看如下情况:
在Python中,小整数对象(通常是-5到256之间的整数)是预先创建并重复使用的。这是为了优化内存使用和性能,因为这些小整数在大多数Python程序中都会频繁使用。所以当你创建一个变量a并赋值为2,然后创建一个变量b并赋值为2,它们实际上都是指向同一个内存对象。
对于大整数(通常是大于256的整数),Python会为每个新的整数创建一个新的对象。所以当你创建一个变量a并赋值为1500,然后创建一个变量b并赋值为1500,它们实际上是指向两个不同的内存对象。这就是为什么id(a)和id(b)返回不同的值。参见下图:
Python语言的变量和赋值情况
Python语言中,所有的 数据 都被称之为 对象。
每个整数、小数、字符串,后面要学的 字典、元组、列表 等, 都是对象。
编程语言为了方便操作数据对象,也需要给对象起一个名字,称之为 变量名 , 有时也简称 变量。
Python中的变量,就是 对象的名称。
Python中,名称 用于指代对象。 名称是通过名称绑定操作来引入的。
【见官方文档 命名与绑定
https://docs.python.org/zh-cn/3/reference/executionmodel.html#naming-and-binding
提示:在Python的官方文档中更多地使用了“名称”(name)这个术语来。但是在实际的编码和交流中,人们也常常使用“变量”(variable)这个术语来描述Python中的名称。】
Python 是一门动态类型的语言,所以我们无须预先声明变量类型,直接对变量赋值即可。
可以使用赋值语句来创建变量并使其引用一个数据。当一个变量表示存储器中的某个值时,也称这个变量引用(reference)了这个值。
赋值语句的基本格式是:
Variable = expression
等号(=)称为赋值运算符(assignment operator)。在这个基本格式中,variable 是变量名,expression 是一个值,或者能够产生一个值的代码。赋值语句执行结束后,位于等号左边的变量将引用位于等号右边的值。
提示:在给变量赋值之前是不能使用变量的。如果在变量被赋值之前就对它进行某种操作,如打印它,将导致一个错误。
【注意: Python 语言中的变量与大多数其他编程语言中的变量的工作方式不同。在大多数编程语言中,变量是保存值的内存位置。在这些编程语言中,当给变量赋值时,该值保存在变量的内存位置。而在 Python 语言中,变量是一个内存位置,它保存另一个内存位置的地址。将值赋给 Python 变量时,该值将存储在与该变量分离的内存位置。变量保存的是保存值的内存位置的地址。这就是为什么在 Python 中,不说变量“保存”一个值,而说变量“引用”一个变量。或者说,Python 中变量是代表存储在计算机存储器中的某个值的名称。】
变量之所以称为“变量”,是因为在程序执行过程中它们可以引用不同的值。下面就看看Python 中变量的赋值和再赋值情况。
创建了一个名为 dollars 的变量,并将 2.75 赋值给它
Dollars = 2.75
给变量 dollars 赋以一个新值99.95,当给变量 dollars 赋以一个新值99.95时,虽然旧值 2.75 依然保存在计算机的存储器中,但是因为没有变量引用它,所以这个值已不能再被使用。参见下图:
注意,当存储器中的值不再被变量引用时,Python 解释器将通过所谓的垃圾收集机制来进行处理,自动地将它们移出存储器。
【在python中,变量保存的是对象(值)的引用,id()函数可以获取变量在内存中的地址。在Python中,值可以放在内存的某个位置(地址),变量用于引用它们,给变量赋一个新值,原值不会被新值覆盖,变量只是引用了新值。顺便说明,Python的垃圾回收机制会自动清理不再被用到的值,所以不用担心计算机内存中充满被“丢弃”的无效的值。】
一定要牢记:在 Python 语言中,变量仅仅是引用存储器中某个数据的名字,并且可以引用任何类型的数据项。
x = 99 语句创建了名为 x 的变量,并赋给它 int 型的值 99。
将一个字符串 'Take me to your leader'赋值给变量 x时,变量 x 不再引用一个 int 型数据,而是引用字符串 'Take me to your leader'。
下面再看看这种情况:
a = 10
b = a
在上述的例子中,我们声明了一个变量 a,其值为 10,然后将 b 也指向 a,这是在内存中的布局是这样的,变量 a 和 b 会指向同一个对象 10,而不是给 b 重新生成一个新的对象。
当执行完 a = a + 10 后,因为整数是不可变对象,所以并不会将 10 变成 20,而是生成一个新的对象 20 ,然后 a 会指向这个新的对象。b 还是指向旧对象 10。
Python 允许你同时为多个变量赋值。例如:
a = b = c = 10
以面示例,创建一个整型对象,值为 10,从后向前赋值,三个变量被赋予相同的数值。
您也可以为多个对象指定多个变量。例如:
a, b, c = 10, 20, " hello"
以面示例,两个整型对象 10 和 20 的分配给变量 a 和 b,字符串对象 “hello” 分配给变量 c。
在Python中的参数传递情况
要讲清在Python中函数的参数传递,需要从Python中函数是什么讲起。
在Python中,函数是一种组织代码的方式,它将一段程序代码封装起来,以便于重复使用。函数可以接收输入参数,并返回一个或多个结果。这种封装可以使代码更加清晰、易读,也有助于代码的重用。Python提供了许多内置函数,如print()、len()等,可以直接使用。编程人员还可以自定义函数。自定义图示如下:
Python中的函数定义使用def关键字,后跟函数名、括号()和冒号。括号中可以放置函数的参数,参数之间用逗号分隔。函数的主体部分在冒号后的缩进块中定义。Python中定义函数语法如下:
def 函数名(参数列表):
函数体
函数体由缩进的代码块组成。Python的函数体包括:
函数体包括以下几个部分:
文档字符串(docstring):文档字符串是可选的,用于描述函数的作用、参数、返回值等信息。它位于函数定义的第一行或第二行,使用三引号('''或""")括起来。文档字符串可以通过help()函数或在交互式环境中使用__doc__属性来访问。
语句(statements):函数体中可以包含任意数量的语句,用于执行具体的任务。这些语句可以是赋值语句、条件语句、循环语句等,根据函数的需求编写。
返回表达式(return expression):函数可以通过return语句返回一个值。返回值是可选的,如果没有return语句或者return后面没有表达式,函数将返回None。当函数执行到return语句时,函数将立即结束,并将返回值传递给调用函数的地方。
下面介绍python参数传递
python官方文档的说法【https://docs.python.org/zh-cn/3/tutorial/controlflow.html#defining-functions 】实参是使用 按值调用(call by value) 来传递的(其中的 值 始终是对象的 引用 而不是对象的值)——实际上,按对象引用调用(call by object reference)调用这种说法更好。具体来说:
如果传入的参数是不可变类型(如数字、字符串、元组),那么在函数体内修改参数的值,并不会影响到原来的变量。因为不可变类型的变量实际上是值的引用,当你试图改变变量的值时,相当于是在创建新的对象。例如:
def change_number(num):
num = 100
x = 10
change_number(x)
print(x) # 输出:10
在上面的例子中,尽管在函数内部num的值被改变了,但是原变量x的值并没有改变。参见下图:
如果传入的参数是可变类型(如列表、字典),那么在函数体内修改参数的值,会影响到原来的变量。因为可变类型的变量存储的是一个地址,当你试图改变变量的值时,实际上是在改变这个地址所指向的内容。例如:
def change_list(lst):
lst.append(100)
x = [1, 2, 3]
change_list(x)
print(x) # 输出:[1, 2, 3, 100]
在上面的例子中,函数内部对参数lst的修改影响到了原变量x的值。参见下图:
附录、
Python变量的认识理解 https://blog.csdn.net/cnds123/article/details/116768499
python函数 https://blog.csdn.net/cnds123/article/details/108179769