作为一个由c/c++转过来的菜鸟,刚接触Python的变量的时候很不适应,应为他的行为很像指针,void* ,不知道大家有没有这样的感觉。其实Python是以数据为本,变量可以理解为标签。作为c/c++的菜鸟,把跟踪变量地址的习惯带入Python,举个小例子说明Python的变量,对象,及参数传递。
1 ‘‘‘例子1‘‘‘ 2 x = 1 3 def fun(x): 4 x = 2 5 return None 6 7 fun(x) 8 print(x)
其实不打印也可以,我们用pycharm单步调试,看一下在每一行执行中,变量x的值的变化,及其地址的变化(其实这句话应该改成:变量x的指向变化更准确)
Python中id()函数,可以返回对象的地址,id()的官方解释是:Return the “identity” of an object,既然是identity,肯定是唯一的;官方又说:CPython implementation detail: This is the address of the object in memory。我们暂时把id()返回值看做是对象在内存中的地址。
第一步:进入debug,在监视窗口,添加对Id(x),和id(y) 的观察,蓝色高亮,表示下一步将执行,我们看到这时,x,y都没有分配地址
第二步:执行下一步,我们发现变量x,开始分配地址, 1392686144,我们记下这个数。
第三步:进入函数中,执行 x = 2语句,我们发现,x的地址已经变成 1392686176,这就是Python 变量的特性,我们不能理解成把变量x赋值为2,而是“名字为x的标签指向对象2”,这样更准确。
第四步:返回fun(x)函数,我们发现X的id()值又变回原来的数字,在这个例子中,我们把局部变量和全局变量用同一个标签指示,当调用函数,进入函数内部执行时,系统会创建堆栈,保留进入函数前的运行环境及数据。进入函数后,有创建了一个同名的标签x,x = 2,把局部标签指向局部对象2,这是局部标签x指向一个新的对象,内存地址肯定变化,当return none,返回函数调用时,堆栈撤销,局部的对象,变量随之撤销,局部标签x也撤销,此时x做回自己,变成全局标签x,依旧指向数字对象1.这就是为什么在函数内部,标签x指向其他对象后,返回调用,又恢复调用前的内存地址。
第一个例子中,从标签x的内存地址变化,帮我们理解Python的变量的行为。
在第二个例子中,我们仍然通过监视标签的内存地址变化,理解参数传递的过程
1 ‘‘‘例子2‘‘‘ 2 a = [] 3 def fun(a): 4 a.append(1) 5 return None 6 7 fun(a) 8 print(a)
第一步:执行完函数调用,参数赋值,蓝色高亮是下一步将要执行的代码。我们发现在这一步,发生了参数赋值,创建堆栈,局部标签x的内存地址与外部标签a的内存地址相同,说明这一步,完成参数赋值,我们是不是可以把Python的“赋值语句”理解为“标签指向”这个动作?从这看,这样理解是可以的。所以“参数赋值”这个动作,可以理解为统一标签指向。
第二步: 当函数返回时,我们发现列表a的地址没有改变,并且列表中元素1得到保留,没有因为局部变量撤销而消失,这回一个典型的通过标签(引用),在局部过程中改变全局变量的例子。Python标签的这种特性是不是很像c++中的引用?是不是很像c中的指针?
总结:Python的变量,我们用标签来理解,参考c的void*,参考c++中的&,Python的赋值动作,可以理解为“标签改变指向”的动作。参数传递过程,是交换标签指向的过程