python申请内存函数_python进阶用法2 【从帮助函数看python内存申请机制】

前言

介绍了四个帮助函数,dir(),help(),type(),id(),通过id()函数进一步分析了python在申请内存方面的效率问题,提到的基本类型有string,list,queue和deque

四个帮助函数

dir()函数

dir()函数是查看函数或模块内的操作方法都有什么,输出的是方法列表。

dir('str')

c7c8fa197d253dca14798197e0ff6ae5.png

也可以查看自己定义的函数

5179db55dfe61028b73bdb4a109c449c.png

help()函数

help()函数是查看函数或模块用途的详细说明,例:

help('str')

2b183d8cc9c19825c8678b7928bab112.png

type()函数

这个很简单了,返回其类型,略

id()函数

对一个对象的引用调用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的地址是相同的!!!

从id()函数看python内存地址申请机制

看个例子:(以下内容来源@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无疑是最快的~

补充,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的内存分配


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部