对象和引用
对象:
内存中存放的一块逻辑的整体数据对象具有唯一的标识符,对象包括属性(Properties)和方法(Methods),属性就是需要记忆的信息,方法就是对象
能够提供的服务。在面向对象(Object Oriented) 的软件中,对象(Object)是某一个类(Class)的实例(Instance)。在python中输入dir(2)
可以知道,在python中2并不是简单的一个整数,而且包含了很多内置的方法。
引用:
引用像指针一样指向一块内存空间,跟c语言中的指针相似。
举例:
整数对象
简单的赋值语句 a = 10000, a 指向了 10000 所在的那块内存。a 引用的对象就是存放 1 的内存地址。is 判断两个变量是否引用同一个对象,id()取得对象的内存地址
a = 10000
b = 10000
print a is b
输出是 False, 可以知道a,b是两个不同的对象,每次创建都会申请新的内存
a = 1
b = 1
print a is b
输出是 True,a,b又是相同的对象了。这是因为在python启动的时候,常用的常数[-5, 256]就建立好,不销毁的对象,也就是a,b都引用相同的>一块内存(存放10000的内存),每次创建这个范围内的整数对象不会申请新的内存。这是个设计上的均衡,用一些内存的浪费,来提高大部分程序>的运行速度
容器对象
python中容器(可以包含其他对象的对象)中存放的对象实际上都是引用
In [17]: a = [[]] * 5
In [18]: a
Out[18]: [[], [], [], [], []]
In [19]: a[1].append(0)
In [20]: a
Out[20]: [[0], [0], [0], [0], [0]]
In [21]: a[0] is a[1]
Out[21]: True
a 列表有五个相同的元素,这五个元素都是对同一个空列表的引用(21行),所以改变其中任何一个元素(不改变对象引用的情况下),所有的五个元>素都会同时进行改变
list 是可变对象,在list扩容的时候有几种方法
In [6]: a = [1]
In [7]: id(a)
Out[7]: 28048488
In [8]: a = a + [2]
In [9]: a
Out[9]: [1, 2]
In [10]: id(a)
Out[10]: 28047696
可以看出使用 + 修改列表,创建了新的列表(对象),a 已经不是原先那个对象,不存放在原先那个内存地址了,进行了大量的内存复制
In [21]: a = [1]
In [22]: id(a)
Out[22]: 20913832
In [23]: a += [2]
In [24]: id(a)
Out[24]: 20913832
使用 += 修改列表,并没有创建新的列表(对象),还存放在原先那个地址。(extend 与 += 几乎相同,只是extend多了调用函数的时间)。
In [29]: a = [1]
In [30]: id(a)
Out[30]: 28048704
In [31]: a.append(2)
In [32]: id(a)
Out[32]: 28048704
使用 append 修改列表,也没有创建新的列表,也是存放在原先那个地址。append会进行提前的内存申请,扩容时会更快。但是 append 只能进行单个元素的扩容,内存消耗较大。
所以列表扩容的时候尽量使用 extend 或者 append(当然 list 如果进行频繁大量空间的扩容,肯定会造成效率问题)。
string 和 tuple 都是不可变对象,所以在进行改变的时候肯定都会创建新的对象。(根据这个原理,可以思考下为什么 string 进行合并的时候为什么建议用join)
函数对象
在 python 中函数也是对象,所以我们可以像整数赋值一样,将函数赋值给一个变量,将函数作为参数传递给另一个函数,我觉得这也是为什么python中存在 decorator 的一个原因
参考:
http://zh.wikipedia.org/wiki/%E5%AF%B9%E8%B1%A1_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6
python群 22507237 的 @冒泡 大神