1. 程式人生 > >python進階用法2 【從幫助函式看python記憶體申請機制】

python進階用法2 【從幫助函式看python記憶體申請機制】

前言

介紹了四個幫助函式,dir()help()type()id(),通過id()函式進一步分析了python在申請記憶體方面的效率問題,提到的基本型別有stringlistqueuedeque

四個幫助函式

dir()函式

dir()函式是檢視函式或模組內的操作方法都有什麼,輸出的是方法列表。

dir('str')

這裡寫圖片描述

也可以檢視自己定義的函式
這裡寫圖片描述

help()函式

help()函式是檢視函式或模組用途的詳細說明,例:

 help('str')

這裡寫圖片描述

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記憶體地址申請機制

如果還是沒有理解的話,接下來看我本地進行的一個非常簡單的一個演示指令碼測試:

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]

queuedeque其實可以很明顯看出,其均是依靠c的連結串列進行開發的(不需要提前申請空間),其地址亦不變化。更改一點之前的錯誤理解,python的list實現不是連結串列,而是動態陣列

當我們使用deque時,可以很明顯看到,我們的時間消耗已經差距很小了,與未提前申請空間的list接近一致,但經多次執行,還是可以發現,最快的依舊是已經申請了空間的list,我麼進一步去了解queuedeque可以發現其無法提前申請空間(可以理解為其職能分別從一端和兩段進行增加值,減值),及無法像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用法2 幫助函式python記憶體申請機制

前言 介紹了四個幫助函式,dir(),help(),type(),id(),通過id()函式進一步分析了python在申請記憶體方面的效率問題,提到的基本型別有string,list,queue和deque 四個幫助函式 dir()函式 dir(

python2)——re模組:正則表示式1

實驗結果輸出文件,包括多項引數(大約幾百個),想把所有的loss value對應的值提取出來,畫出曲線圖,這就需要用到正則表示式,基於此,開始學習正則表示式。 正則表示式:可匹配文字片段的模式 萬用字元:句點.(.ython與jpython與python與 ython都匹配,但不與ython

python2)——re模組:正則表示式2

re.split 根據模式來分割字串 import re text='a, b,,,,c d' print(re.split('[, ]+', text)) #re.split:以空格和字串分割字元 re.findall 返回列表,包含所有與給定模式匹配的子串 import re

Python 用法 (持續更新)

裝飾器(Decorator) Python 的裝飾器是任何可呼叫物件(callable object),用於修改函式(Function)或類(Class)。按照用途可分為: 函式裝飾器 類裝飾器 裝飾器的介面定義可概括為: 接收某個函式或類的引用作為引數; 修改該函式或類並返回

python—OpenCV之常用影象操作函式說明

文章目錄 cv2.threshold cv2.bitwise_and cv2.bitwise_or cv2.bitwise_not cv2.inRange cv2.resize cv2.adaptiveThreshold cv2

Python丨如何建立你的第一個Python元類?

摘要:通過本文,將深入討論Python元類,其屬性,如何以及何時在Python中使用元類。 Python元類設定類的行為和規則。元類有助於修改類的例項,並且相當複雜,是Python程式設計的高階功能之一。通過本文,將深入討論Python元類,其屬性,如何以及何時在Python中使用元類。本文介紹以下概念:

Python自動化開發課堂筆記Day08 - Python(面向對象的高級用法,網絡編程)

sta 自然 log 報錯 面向 read urn total 析構函數 面向對象的高級用法 1. __str__ 只要執行打印對象的操作,就會觸發該對象類中的__str__方法(也就是對象的綁定方法)它是一種默認的方法,默認的打印輸出為<__main__.Foo o

pythonmap, filter, reduce用法

map map會將一個函式對映到一個輸入列表的所有元素上面。 map(function_to_apply, list_of_inputs) map可以讓我們用一種簡單而漂亮得多的方式來實現。 items = [1,2,3,4,5] squared = lis

python*args 和 **kwargs的用法

參考連結:https://eastlakeside.gitbooks.io/interpy-zh/content/args_kwargs/Usage_args.html 變數名不是最重要的,前面兩個*才是最重要的。 *args用法 *args用來發送一個非鍵值對的可變數量的引

Python自動化開發課堂筆記Day06 - Python(類)

擴展性 程序 lex 類名 人物 優點 ini 參數 self. 類與對象 面向過程的程序設計:  優點:極大的降低了程序的復雜度  缺點:一套流水線或者流程就是用來解決一個問題,生產汽水的流水線無法生產汽車,即使能,也是得大改,改一個組件,牽一發而動全身面向對象的程序設計

Python----*args和**kwargs,裝飾器的用法

pop reference value -- margin zoj 是什麽 ec2 多線程 關於多線程的一個問題 svn提交代碼的時間是什麽時候? 大家都開始C++0x了,我也來湊熱鬧,今天的主題是《調侃rvalue-reference》 新手c語言,求助break問

Python初學者第二十三天 函數2)裝飾器

分享 username 執行 ... ret 傳參 als print n) 23day 裝飾器: 1、用戶不執行前不調用函數,在調用的時候再執行函數 a、傳函數時不加參數 user_status = False # 用戶登錄了就把這個改成True def login(

python之函數的2-1)

加載 gif pre span 進行 文件 chang ret 16px 動態參數: 一,*args:args是元祖,它包含了所有的位置參數。 1,第一種形參中只含*args。 def func(*args): print(*args,type(args)) f

學習筆記python 特性

可能 pytho red nbsp python blog 有一個 自省 blue __slots__魔法 在Python中,每個類都有實例屬性。默認情況下Python用一個字典來保存一個對象的實例屬性。這非常有用,因為它允許我們在運行時去設置任意的新屬性。 然而,對於有

Python第一篇Python簡介

代碼 簡潔 處理 ros 進一步 基礎 得到 運行速度 動態 Python簡介 1.Python的由來 Python是著名的“龜叔”Guido van Rossum在1989年聖誕節期間,為了打發無聊的聖誕節而編寫的一個編程語言。 2.C 和 Python、Java、C#等

Python第九篇裝飾器

turn spa none app light fun rap log python 什麽是裝飾器 裝飾器本身就是函數,並且為其他函數添加附加功能 裝飾器的原則:1.不修改被裝飾對象的源代碼 2.不修改被裝飾對象的調用方式裝飾器=高階函數+函數嵌套+閉包 # res=t

Python第十篇模塊(上)

path 變量 屬性 一個 第三方 sys pre 應用程序 bsp ·一、模塊 模塊就是一組功能的集合體,我們的程序可以導入模塊來復用模塊裏的功能。為了編寫可維護的代碼,我們把很多函數分組,分別放到不同的文件裏,這樣,每個文件包含的代碼就相對較少,很多編程語言都采用這種組

Python-----property用法(實現了get,set,delete三種方法)

一、可以利用property對靜態屬性的進行修改操作,包括設定和刪除屬性   1、呼叫靜態屬性===>在靜態屬性函式前先用@property;   2、設定靜態屬性===>在靜態屬性函式前加上@靜態屬性函式名.setter,同時靜態屬性函式要加上value引數;   3、刪除靜態屬性===>

python(lxml的用法

                   本節處理的檔案如下,檔名為:webhtml.html <!DOCTYPE html> <html> <head> <

python(爬蟲 BeautifulSoup用法

操作演示檔案:   檔名:    webhtml.html <!DOCTYPE html> <html> <head> <title>漏斗圖</title> <script type="