对象和类型
Python中将一切都视为对象。在语法的很多细节上通过上层封装掩盖了底层逻辑,故需要仔细理解Python中底层的操作机制,才能避免在一些基本问题上出错。
对象
对象就是内存中的一块存放着数据的区域,拥有唯一的标识符。
在Python中,任意元素都是对象,而每个对象都有标识符、类型和值等属性。
使用id()函数可以以查看任意元素的标识符。
如上述变量n的id和5的id就是相同的,通常情况下是内存地址。
二值交换
在cpp中,我们如何交换两个变量的值?
通常做法是需要借助第三个临时变量:
int a,b,t;
t = a;
a = b;
b = t;
而在Python中,似乎拥有一种更加简单,无需通过临时变量的方法即可交换:
a, b= b, a;
从内存角度来看,这两种语言在交换时是存在区别的。
cpp
有三个内存区域,我们可以想象成三个抽屉,这三个抽屉分别贴上了a,b,t的标签(变量名)。
在交换a,b元素的时候,先得a抽屉里的内容复制一份到t中;
再把a抽屉里面的内容从b中复制一份,覆盖掉a抽屉原本的内容;
最后将t中存放a原本内容的副本,复制到b中,将b原本的内容覆盖。
完成这些步骤后,a中放着b之前元素的复制,b中放着a之前元素的复制。如此一来,就完成了二值交换的过程。在内存中,原本a元素所在的地址中的内容确确实实变成了b的值,b同理。
Python
仍然可以将内存中的两个区域想象成两个抽屉,抽屉上贴着两个标签a,b。Python的交换方法非常简单,其直接把这两个抽屉的标签“撕了下来”,将原本的a抽屉贴上了b标签,b抽屉贴上了a标签。
这样一来,再去查询b抽屉的时候,实际上就是之前的a抽屉,其中的内容也是之前a抽屉的值,现在的a抽屉同样如此。这样一来,看上去就完成了a与b的交换,而实际上在内存中,原本a和b地址所存放的数值并没有交换,只是这些内存地址的名字发生了变化。
在cpp中,变量名和内存区域是绑定的,而在Python中,内存区域和值是绑定的,这是两者最大的区别。
通俗来说:若把cpp的内存区域看成一个抽屉,那么这个抽屉上面的标签是“撕不下来”的,但是里面的内容是可以被更改的。而Python恰恰相反,这个抽屉和里面的内容是绑定的,但是它的标签却可以随意更改。
在Python中,任何一个内存区域和其中的值永远被视为一体,叫做对象。
每个对象拥有唯一的标识符(通常是内存地址)。
每一个对象可以拥有数个“名字”,所谓的变量也只不过是某个对象的一个“名字罢了”。
也就是存在多个变量指向同一个对象(指向内存当中同一个抽屉)。
如上图,看似存在abcdf五个变量,倘若这五个变量的值都是1,那么他们实际上都指向同一块存放着“1”这个值的内存区域,故他们的标识符都相等。即实际上这五个变量名只不过是“1”这个对象的马甲而已。
数据和内存区域密不可分,被叫做对象,这一点和对象名与内存区域密不可分,而数据可变的cpp完全不同。
cpp的情况:
在cpp中,虽然这五个变量都是5,但是5只是这些“抽屉中的值”,这5个变量实实在在的和五个抽屉所绑定。
从以上的例子,可以总结出Python对象的几个特点和性质:
- 变量是对象的引用,它只是关联到对象的一个名字。
- 在对变量进行赋值操作时,不会复制对象的值(即不会改变内存中某一个区域里面存放的内容),只会改变引用对象。
- 在Python中,一切皆为对象。对象占一定的存储空间,并拥有标识符、类型和值等属性。
变量看似会变,实际上只是对于内存中不同不变数据的引用而已。
可变类型和不可变类型
上述所举的数字属于不可变类型,即一旦在内存中生成某个存放某一数值的内存区域后,这一内存区域的值就不能再发生改变。这一点是由Python内存区域和值的紧密结合性而定。
Python中也存在一些值可以发生改变的类型。
- 可变类型:列表、字典、集合。
- 不可变类型:数字、字符串、元组。
身份运算符
x is y :如果x和y引用了同一个对象,则生成True,否则生成False。
x is not y:如果x和y不是同一个对象则生成True,否则生成False。
通常情况下会使用身份运算符比较可变类型,因为可变类型存在值相等但是标识符不同的情况(即存在内存中不同区域存放了相同值的情况)
赋值语句
前面的例子可以看出,Python中由于是将内存区域与值进行绑定,故对于不可变类型不存在修改内存区域内容的问题,那么赋值语句实际上并没有同cpp一般,改变了某一个变量所引用的内存区域中的数据内容。
可以总结出赋值语句的一些特性:
- 等号右边的值没有复制到等号左边。
- 等号左边的变量名绑定到等号右边的对象(使变量可盈引用对象)。
- 等号左边的变量名如果是第一次使用,则新生成一个变量。
del语句
del语句可以解除一个名称与对象的引用,如果该名称是对象的唯一引用,那么程序就会释放该对象的内存。
个人心得
在初学c和cpp的时候,最头疼的无疑是数据类型,要仔细的分辨各种数据类型的数据范围,也要弄清楚语言在内存当中具体是如何操作的。数据类型的那张大表已经成了任何c/cpp教材第一章或者第二章的老主顾。
但Python显然通过对象和引用关系自动封装了“数据类型”一说,在创建任何一个变量的时候,Python会通过引用的方式自动给你连接合适的数据类型,对于数据的修改也变成了“自动引用”新的对象,就不存在所谓的数据类型的大问题。
当然,作为程序的使用者,我们仍然需要知道Python中对于各种数据类型其的一些细节,但是无疑,花开两朵,各表一枝,Python与cpp在底层对于内存数据的使用上决定了其的学习方式和关注点的不同。