Python 列表内存浅析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 列表内存浅析相关的知识,希望对你有一定的参考价值。

参考技术A

序列是Python中最基本的数据结构。序列是一种数据存储方式,用来存储一系列的数据。

在内存中,序列就是一块用来存放多个值的连续的内存空间。比如一个整数序列[10,20,30,40]

序列中的每个元素都分配一个数字 - 它的位置,或索引。第一个索引是0,第二个索引是1,依此类推。

列表:用于存储任意数目、任意类型的数据集合。

列表是内置可变序列,是包含多个元素的有序连续的内存空间。列表定义的标准语法格式:

其中,10,20,30,40 这些称为:列表a的元素。

列表中的元素可以各不相同,可以是任意类型。比如:a = [10,20,"abc",True,[]]

当列表增加元素时,列表会自动进行内存管理,减少了程序员的负担。但是列表元素大量移动,效率低所以一般建议在尾部添加。

本地电脑运行结果:

列表是可变数据类型,地址不变,值可变。因此,添加新的值之后,地址也是不变的。

解析:在索引2处要引用50这个元素

申请了8个内存空间但是list实际用来存储元素只使用了其中5个内存空间
insert的时间复杂度是O(n)

pop () 方法 删除并返回指定位置的元素,如果未指定位置则默认操作

pop () 方法 删除并返回指定位置的元素,如果未指定位置则默认操作

结果运行:

python变量内存浅析

1. Python变量

   如果把单一值变量称为一维变量,把可以扩展元素的变量称为多维变量,则python的变量可以划分如下:

变量维度

Python变量

说明

一维

数字

int(有符号整型)

数字类型可以做转换

long(长整型[也可以代表八进制和十六进制])

float(浮点型)

complex(复数)

字符串

字符串有丰富的运算符和内嵌函数;

有格式化输出;

二维

列表

除了元祖外,列表和字典可以灵活的扩展元素

元组

字典

2. Python变量的赋值处理

   这里主要分析情况为,将原始变量赋值给新变量后,二者的变化关联情况,比如:原始变量为varold,新变量varnew = varold,之后varnew发生了变化,那么varold如何变化,二者的地址情况如何?这里给出测试用例:

   用例1

def whole_modify(value_old, value_new):
    #before
    obj_old = value_old
    obj_new = obj_old
    print(obj_old, id(obj_old))
    print(obj_new, id(obj_new))
    #after
    obj_new = value_new
    print(obj_old, id(obj_old))
    print(obj_new, id(obj_new))
print(‘value:‘)
whole_modify(‘aaa‘, 9)
print(‘list:‘)
whole_modify([‘a‘, 2], [3, ‘b‘])
print(‘Dictionary:‘)
whole_modify({1:‘a‘, ‘b‘:2}, {3:‘c‘})

   测试结果:

value:

(‘aaa‘, 38126200)

(‘aaa‘, 38126200)

(‘aaa‘, 38126200)

(9, 4014992)

list:

([‘a‘, 2], 43066072)

([‘a‘, 2], 43066072)

([‘a‘, 2], 43066072)

([3, ‘b‘], 41421080)

Dictionary:

({1: ‘a‘, ‘b‘: 2}, 39583440)

({1: ‘a‘, ‘b‘: 2}, 39583440)

({1: ‘a‘, ‘b‘: 2}, 39583440)

({3: ‘c‘}, 39599104)

   用例1whole_modify是对新变量的整体修改,即直接用=号将一个全新的数据整体赋值给新变量,从测试情况看,结论是:

   不论一维还是二维变量,通过=号整体赋值后,新变量会重新分配内存,值为新赋给的值;原变量保持不变。

   用例2

print("list:")
obj_old = [‘a‘, 2]
obj_new = obj_old
print(obj_old, id(obj_old))
print(obj_new, id(obj_new))#after
obj_new[0] = 3
print(obj_old, id(obj_old))
print(obj_new, id(obj_new))

print("dictionary:")
obj_old = {1:‘a‘, ‘b‘:2}
obj_new = obj_old
print(obj_old, id(obj_old))
print(obj_new, id(obj_new))#after
obj_new[‘b‘] = ‘newvaluestr‘
print(obj_old, id(obj_old))
print(obj_new, id(obj_new))

   测试结果:

list:

([‘a‘, 2], 39932832)

([‘a‘, 2], 39932832)

([3, 2], 39932832)

([3, 2], 39932832)

dictionary:

({1: ‘a‘, ‘b‘: 2}, 40107728)

({1: ‘a‘, ‘b‘: 2}, 40107728)

({1: ‘a‘, ‘b‘: ‘newvaluestr‘}, 40107728)

({1: ‘a‘, ‘b‘: ‘newvaluestr‘}, 40107728)

   用例2中,主要是对二维变量内部元素做修改(一维变量无需测试),从测试情况看,结论是:

   老二维变量被赋给新二维变量后,对二者任何一个的内部元素修改,二者的地址不会发生改变,但值会同步被修改。

3. 分析

   Python的变量赋值(包括传参,返回参数等场景),与c/c++区别很大,而且很难用一句话概括其用法。上面两个测试用例主要是从变量类型的角度来区分,具有很强代表性,可以引申到很多场景辅助理解。

   很多人将python的变量赋值当做引用看待,这应该是有问题的,比如上面用例1中,很显然就不是引用,因为新变量地址和值都变化了,而老变量完全保持不变。而c/c++引用的概念是,新老变量地址不变,值同步变化。

   用例2中对二维变量做局部修改,会发现新老变量的局部元素的地址和值都同步变化了(变量本身地址不变),这个和引用相同的地方是值同步变化了,不同的是变量地址也同步变化了。

   Python的这种处理,应该是基于内存效率的考虑,虽然python应用开发中不用考虑变量地址与内存回收,但如果对变量赋值的内存情况完全忽视,则很容易产生意外的结果。
































以上是关于Python 列表内存浅析的主要内容,如果未能解决你的问题,请参考以下文章

浅析Python垃圾回收机制

Python面试题目之Python的复制和赋值浅析

urlopen内存泄漏浅析

Python中的字典有序无序浅析

内存管理 浅析 内存管理/内存优化技巧

Linux 内存池源码浅析