先看一段源码
a = [['_']*3 for i in range(3)]
a[1][2] = 1
print(a)
b = [['_']*3]*3
b[1][2] = 1
print(b)
c = []
for i in range(3):
row = ['_']*3
c.append(row)
c[1][2] = 1
print(c)
d = []
row = ['_']*3
for i in range(3):
d.append(row)
d[1][2] = 1
print(d)
[['_', '_', '_'], ['_', '_', 1], ['_', '_', '_']]
[['_', '_', 1], ['_', '_', 1], ['_', '_', 1]]
[['_', '_', '_'], ['_', '_', 1], ['_', '_', '_']]
[['_', '_', 1], ['_', '_', 1], ['_', '_', 1]]
这里涉及到了深复制和浅复制的区别,需要弄清楚栈和堆的区分, 因为栈与堆的运行速度是数量级差异,而且栈还有可能把数据直接调入寄存器进行读写,又因栈内空间十分有限,所以有了深复制与浅复制,默认浅复制. <深入理解计算机系统> 后有详细的栈堆实现,我目前还是没有看到,但第一章可以有大概的描述.
再看下面的代码,则可以推论出 * 的重载运算符, append 都是浅复制, 包括list的实现也是指针的集合. list我的现阶段的理解是, 指向堆内数据指针的集合, 而且因为把这些指针的地址排列成连续的内存结构, 所以可以用索引访问. 因为c++的 链表 二叉树因为内存不连续,所以无法用索引访问, 由此推出以上结论.
a = [['_']*3 for i in range(3)]
a[1][2] = 1
print(a)
b = [['_']*3]*3
b[1][2] = 1
print(b)
for i in range(3):
for j in range(3):
print(id(b[i][j]))
c = []
for i in range(3):
row = ['_']*3
c.append(row)
c[1][2] = 1
print(c)
d = []
row = ['_']*3
for i in range(3):
d.append(row)
print(d)
d[1][2] = 1
print(d[0][0] is d[0][1])
print(id(row))
for i in range(3):
for j in range(3):
print(id(d[i][j]))
[['_', '_', '_'], ['_', '_', 1], ['_', '_', '_']]
[['_', '_', 1], ['_', '_', 1], ['_', '_', 1]]
2171792199896
2171792199896
1609591840
2171792199896
2171792199896
1609591840
2171792199896
2171792199896
1609591840
[['_', '_', '_'], ['_', '_', 1], ['_', '_', '_']]
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
True
2171797620552
2171792199896
2171792199896
1609591840
2171792199896
2171792199896
1609591840
2171792199896
2171792199896
1609591840
相同的内存地址,说明了其是堆区的内存地址,但是栈区的指针地址不知道用什么方法可以访问到,可能是因为栈区 读写太快,不便取址的原因没有设计还是什么原因, 需要日后解决.
可以确定是,python设计中,绝大部分都应该是以浅复制以及赋值为主, 比如int , float 类型数据,因为读写占用资源并不多, 若以引用的形式进行操作, 性价比并不高, 或许寻址花费的时间还要超过运算时间, 所以 此类数据一般都为 对其值进行操作,而且可以让其在栈内 甚至是在寄存器内读写(记得c/c++ 都有强制在寄存器读写的作用域命令关键字). 而对于list 以及大型数据类型, 就是需要用地址进行操作, 可能是因为性价比高, 寻址花费时间远远低于其直接读写时间, 所以需要在堆内进行读写.
对于衍生出的疑惑:
- CPU内有寄存器 L1 或许 还有外围的内存 以至于硬盘作为其上一级高速缓存的一级缓存, 那么堆栈是否有物理上的差异,还是仅仅是虚拟上的差异, 譬如栈总是在高一级缓存实现.
- 读写是通过总线进行的, 那么如同知乎上说的 命中率问题的根源是什么. 栈的高效率 到底是由于物理的差异,还是所谓的命中率原因.
- 对于指针本身在栈区的地址的获取方法是什么.
看完<深入理解计算机系统> 再回来续写