介绍了四个帮助函数,dir()
,help()
,type()
,id()
,通过id()函数进一步分析了python在申请内存方面的效率问题,提到的基本类型有string
,list
,queue
和deque
dir()函数是查看函数或模块内的操作方法都有什么,输出的是方法列表。
dir('str')
help()函数是查看函数或模块用途的详细说明,例:
help('str')
这个很简单了,返回其类型,略
对一个对象的引用调用id()
函数,可以得到该对象的标识符(dentity).该标识符是一个整数,它保证在该对象的生命周期内是唯一的和恒定的.具有不重叠生命周期的两个对象具有相同的id()
值.
PS: 在CPython
实现细节:标识符(dentity)为对象在内存中的地址. 在Python
中一切皆对象,变量中存放的是对象的引用.字符串常量和整型常量都是对象.
举个例子:
>>> a = 1
>>> b=2
>>> c =1
>>> id(a)
1521120064
>>> id(b)
1521120096
>>> id(c)
1521120064
不知道,在看这个dentity
的时候,有没有发现a与c的地址是相同的!!!
看个例子:(以下内容来源@Unname_Bao 关于python3中整数数组转bytes的效率问题)
如果还是没有理解的话,接下来看我本地进行的一个非常简单的一个演示脚本测试:
import time
t1 = time.time()
astr = 'a'
for x in range(1,2000000):
astr = astr + str(x)
astr
t2 = time.time()
print(t2-t1)
#10.028913259506226
#[Finished in 10.2s]
import time
t1 = time.time()
astr = list('1'*2000000)
for x in range(1,2000000):
astr[x]=str(x)
bstr = str(astr)
t2 = time.time()
print(t2-t1)
#0.8323781490325928
#[Finished in 1.1s]
为什么差距如此之大呢?这就回到了我们最初提到的a
,b
,当值改变,会重新去申请内存空间(id改变)。在这第一个例子中,我们不停地改变astr
的值,astr
即不停地申请内存空间,此过程消耗了大量的时间!!!第二个例子中,我们一次申请了够所有变量使用的内存空间地址,免去了每次申请,所以大大加快了运行速度!!!
感谢@一个闲散之人的闲散
更进一步的分析,
影响其效率问题的核心根本在于list
到底是基于链表的数据结构还是基于线性表的数据结构。线性表的话为了腾出足够连续空间需要改变表头的内存位置,也就造成了id
的改变,对于链表而言,则只需要申请一个结点大小的内存量,没必要对表头的内存位置动手脚。
关于list
的数据结构,从知乎上get到的结果是线性表形式的数据结构,于是乎我又做了以下3个测试:
1、不提前申请空间的queue
import time
import queue
t1 = time.time()
astr = queue.Queue()
for x in range(1,2000000):
astr.put(str(x))
bstr = str(astr)
t2 = time.time()
print(t2-t1)
# 4.525705337524414
# [Finished in 4.8s]
2、不提前申请空间的deque
import collections
import time
t1 = time.time()
astr = collections.deque()
for x in range(1,2000000):
astr.append(str(x))
bstr = str(astr)
t2 = time.time()
print(t2-t1)
# 0.938164234161377
# [Finished in 1.3s]
3、不提前申请空间的list
import time
t1 = time.time()
astr = []
for x in range(1,2000000):
astr.append(str(x))
bstr = str(astr)
t2 = time.time()
print(t2-t1)
# 0.9456796646118164
# [Finished in 1.2s]
另做个测试:
import collections
import queue
print("Deque ID:")
astr1 = collections.deque()
for x in range(1,5):
astr1.append(str(x))
print(id(astr1))
print("Queue ID:")
astr2 = queue.Queue()
for x in range(1,5):
astr2.put(str(x))
print(id(astr2))
print("list ID:")
astr3 = []
for x in range(1,5):
astr3.append(str(x))
print(id(astr3))
# Deque ID:
# 1206229307464
# 1206229307464
# 1206229307464
# 1206229307464
# Queue ID:
# 1206225595416
# 1206225595416
# 1206225595416
# 1206225595416
# list ID:
# 1206229266760
# 1206229266760
# 1206229266760
# 1206229266760
# [Finished in 0.2s]
queue
、deque
其实可以很明显看出,其均是依靠c的链表
进行开发的(不需要提前申请空间),其地址亦不变化。更改一点之前的错误理解,python的list
实现不是链表,而是动态数组
当我们使用deque
时,可以很明显看到,我们的时间消耗已经差距很小了,与未提前申请空间的list
接近一致,但经多次运行,还是可以发现,最快的依旧是已经申请了空间的list
,我么进一步去了解queue
和deque
可以发现其无法提前申请空间(可以理解为其职能分别从一端和两段进行增加值,减值),及无法像list
一样可以通过list[下标]
直接取值,所以综上所述,list无疑是最快的~
>>> test = []
>>> test.__sizeof__()
40
>>> test.append('a')
>>> test.__sizeof__()
72
>>> test.append('a')
>>> test.__sizeof__()
72
>>> test.append('a')
>>> test.__sizeof__()
72
>>> test.append('a')
>>> test.__sizeof__()
72
>>> test.append('a')
>>> test.__sizeof__()
104
>>>
参考链接:Python中list的内存分配