python(2): 动态数据类型系统(内容存疑,只是我这么理解)


                python使用动态数据类型系统。在python之中所有变量都是引用型的。这与c中是不同的。来看下面一段c代码:
  • int a,b;
    • a = 3;
    • b = a;来看编译后的情况:第一行定义了两个整型变量,总共的大小一般是64bit。生成的语句应该是向系统申请64位的栈内存,也就是如果接下来还要分配空间的话,就得从第65bit开始了。假设a是在第1位到第32位,b在第33位到第64位。第二句生成的指令类似于,把第1位到第32位置为代表"3"的二进数,也就是“00000000 00000000 00000000 00000011”。第三句把第33位到第64位的数置成跟第1位到第32位一样,也就是所谓的复制。在使用一个变量对另一个变量进行赋值的进候,应该有一个取值的动作,在这里就是把a的值取出,复制出来。这里用的栈内存。再来看堆内存:
    • int* a;
    • int* b;
    • a = new int;
    • b = new int;
    • *a = 3;
    • b = a;
    • *a = 4;前两行定义了两个变量,类型为int*。这么说是因为程序员或者编译器是这么看的,对于电脑来说,它就是留出64bit内存来放这两个东西,反正不管是什么,都只是0和1组成的串。还是像前面一样,假设假设a是在第1位到第32位,b在第33位到第64位,这些都是栈内存。第3行,在堆内存中分配32bit空间,并把这32位空间的首地址(比如是:0x0fff)写入在代表a的第1位到第32位中。第4行也类似。第5行,表示把“地址a”指向的32位堆内存置为“3”。第6行,就像上面说的,把栈内存中第1位到第32位复制到第33位到第64位。这是一个地址,也就是0x0fff,而不是“3”.到此,可以看到,a和b都指向了内存中同一个地方,也就是0x0fff。关键是第7行,这行把"地址a"指向的32位堆内存置为“4”,这时由于b也向了同一个地方,*b所代表的值也已经变成了“4”。
      总之,可以说,一个变量标识符就可以看成是一块内存空间。在编译完成后,所谓的这些标识符都不存在了,都被类似于“把内存第...位到第...位的内容置为...”这样的指令代替了。对于电脑硬件来说,内存里的东西都是0和1组成的串,相互之间没有什么区别,对于硬件的操作来说,只要提供从哪个bit开始、操作多少bit、每位的内容是什么、是0还是1的信息就可以了。至于比如说“第1位到第32位代表a”,这是编译器和程序的事。另一个程序也可以把相同的串解释成“老婆快要生孩子了”。由此也可以推断出,编译器在编译程序时,会维护一个变量名到首地址的映射表,当然也会有长度信息。
      下面来看python中类似的代码:
    • a = 3
    • b = a
    • a = 4各行的意思一目了然,但是在由于a和b都是引用型变量,就跟c中的*a和*b中的a和b一样。容易让人误以为现在b的值也变成了"4",其实在python中,b的值仍然为“3”。首先要把握一点,在python中一切都是对象,不管是变量还是常量,也就是a是一个对象,"3"也是一个对象。“=”的意思是把a指向了"3"这个对象。比如有下面这样一行代码:
      3
      这一行代码只有一个数字“3”,但系统也会为它分配内存空间,只不过是在分配后就立刻丢弃了,因为没有人用它。
      所以"a=3"在python中的实现过程是这样的:
      创建a对象,创建"3"这个整数对象,把“3”这个整数对象的首地址放在a中。
      所以,第3行代码的意思是把a指向"4"这个对象。那么由于b指向的“3”对象仍在那儿,b的值仍为3。
      当然,这也有例外,比如说有一个:
      a = [1,2,3]b = aa[0] = 4这时:>>> print a[4,2,3]>>> print b[4,2,3]
      可以看到b的值也变了。这也好理解。
      前面也说到了,在python中一个资源不用后,立刻就会回收了。这跟java是不一样,python的机制非常明确,不用就回收了。而在java中,垃圾收集器是不定期启动的,这样的话java就可以等到资源废弃到一定程度后在启动垃圾收集器进行一次大扫除,因为启动垃圾收集器系统开销不小,java的方式应该会更有效率。