代碼與圖詳解性能之Python集合類型(list tuple dict set generator)

来源:http://www.cnblogs.com/cfang90/archive/2016/12/25/6220956.html
-Advertisement-
Play Games

Python內嵌的集合類型有list、tuple、set、dict。 列表list:看似數組,但比數組強大,支持索引、切片、查找、增加等功能。 元組tuple:功能跟list差不多,但一旦生成,長度及元素都不可變(元素的元素還是可變),似乎就是一更輕量級、安全的list。 字典dict:鍵值對結構哈 ...


Python內嵌的集合類型有list、tuple、set、dict。 列表list:看似數組,但比數組強大,支持索引、切片、查找、增加等功能。 元組tuple:功能跟list差不多,但一旦生成,長度及元素都不可變(元素的元素還是可變),似乎就是一更輕量級、安全的list。 字典dict:鍵值對結構哈希表,跟哈希表的性質一樣,key無序且不重覆,增刪改方便快捷。 set:無序且不重覆的集合,就是一個只有鍵沒有值的dict,Java的HashSet就是採用HashMap實現,但願python不會是這樣,畢竟set不需要value,省去了很多指針。   Generator: 稱之為生成器,或者列表推導式,是python中有一個特殊的數據類型,實際上並不是一個數據結構,只包括演算法和暫存的狀態,並且具有迭代的功能。   先看看它們的記憶體使用情況,分別用生成器生成100000個元素的set, dict, generator, tuple, list。消耗的記憶體dict, set, list, tuple依次減少,生成的對象大小也是一樣。由於generator並不生成數據表,所以不需要消耗記憶體:
import sys
from memory_profiler import profile

@profile
def create_data(data_size):
    data_generator = (x for x in xrange(data_size))
    data_set = {x for x in xrange(data_size)}
    data_dict = {x:None for x in xrange(data_size)}
    data_tuple = tuple(x for x in xrange(data_size))
    data_list = [x for x in xrange(data_size)]
    return data_set, data_dict, data_generator, data_tuple, data_list

data_size = 100000
for data in create_data(data_size):
    print data.__class__, sys.getsizeof(data)

Line #    Mem usage    Increment   Line Contents
================================================
     4     14.6 MiB      0.0 MiB   @profile
     5                             def create_data(data_size):
     6     14.7 MiB      0.0 MiB       data_generator = (x for x in xrange(data_size))
     7     21.4 MiB      6.7 MiB       data_set = {x for x in xrange(data_size)}
     8     29.8 MiB      8.5 MiB       data_dict = {x:None for x in xrange(data_size)}
     9     33.4 MiB      3.6 MiB       data_tuple = tuple(x for x in xrange(data_size))
    10     38.2 MiB      4.8 MiB       data_list = [x for x in xrange(data_size)]
    11     38.2 MiB      0.0 MiB       return data_set, data_dict, data_generator, data_tuple, data_list
 
<type 'set'> 4194528
<type 'dict'> 6291728
<type 'generator'> 72
<type 'tuple'> 800048
<type 'list'> 824464

 

再看看查找性能,dict,set是常數查找時間(O(1)),list、tuple是線性查找時間(O(n)),用生成器生成指定大小元素的對象,用隨機生成的數字去查找:

import time
import sys
import random
from memory_profiler import profile

def create_data(data_size):
    data_set = {x for x in xrange(data_size)}
    data_dict = {x:None for x in xrange(data_size)}
    data_tuple = tuple(x for x in xrange(data_size))
    data_list = [x for x in xrange(data_size)]
    return data_set, data_dict, data_tuple, data_list

def cost_time(func):
    def cost(*args, **kwargs):
        start = time.time()
        r = func(*args, **kwargs)
        cost = time.time() - start
        print 'find in %s cost time %s' % (r, cost)
        return r, cost  #返回數據的類型和方法執行消耗的時間
    return cost

@cost_time
def test_find(test_data, data):
    for d in test_data:
        if d in data:
            pass
    return data.__class__.__name__

data_size = 100
test_size = 10000000
test_data = [random.randint(0, data_size) for x in xrange(test_size)]
#print test_data
for data in create_data(data_size):
    test_find(test_data, data)

輸出:
----------------------------------------------
find in <type 'set'> cost time 0.47200012207
find in <type 'dict'> cost time 0.429999828339
find in <type 'tuple'> cost time 5.36500000954
find in <type 'list'> cost time 5.53399991989
100個元素的大小的集合,分別查找1000W次,差距非常明顯。不過這些隨機數,都是能在集合中查找得到。修改一下隨機數方式,生成一半是能查找得到,一半是查找不到的。從列印信息可以看出在有一半最壞查找例子的情況下,list、tuple表現得更差了。
def randint(index, data_size):
    return random.randint(0, data_size) if (x % 2) == 0 else random.randint(data_size, data_size * 2)

test_data = [randint(x, data_size) for x in xrange(test_size)]

輸出:
----------------------------------------------
find in <type 'set'> cost time 0.450000047684
find in <type 'dict'> cost time 0.397000074387
find in <type 'tuple'> cost time 7.83299994469
find in <type 'list'> cost time 8.27800011635

元素的個數從10增長至500,統計每次查找10W次的時間,用圖擬合時間消耗的曲線,結果如下圖,結果證明dict, set不管元素多少,一直都是常數查找時間,dict、tuple隨著元素增長,呈現線性增長時間:

import matplotlib.pyplot as plot
from numpy import *

data_size = array([x for x in xrange(10, 500, 10)])
test_size = 100000
cost_result = {}
for size in data_size:
    test_data = [randint(x, size) for x in xrange(test_size)]
    for data in create_data(size):
        name, cost = test_find(test_data, data) #裝飾器函數返回函數的執行時間
        cost_result.setdefault(name, []).append(cost)

plot.figure(figsize=(10, 6))
xline = data_size
for data_type, result in cost_result.items():
    yline = array(result)
    plot.plot(xline, yline, label=data_type)

plot.ylabel('Time spend')
plot.xlabel('Find times')

plot.grid()

plot.legend()
plot.show()

迭代的時間,區別很微弱,dict、set要略微消耗時間多一點:

@cost_time
def test_iter(data):
    for d in data:
        pass
    return data.__class__ .__name__

data_size = array([x for x in xrange(1, 500000, 1000)])
cost_result = {}
for size in data_size:
    for data in create_data(size):
        name, cost = test_iter(data)
        cost_result.setdefault(name, []).append(cost)

#擬合曲線圖
plot.figure(figsize=(10, 6))
xline = data_size
for data_type, result in cost_result.items():
    yline = array(result)
    plot.plot(xline, yline, label=data_type)  

plot.ylabel('Time spend')
plot.xlabel('Iter times')

plot.grid()

plot.legend()
plot.show()

刪除元素消耗時間圖示如下,隨機刪除1000個元素,tuple類型不能刪除元素,所以不做比較:

@cost_time
def test_delete(test_data, data):
    for d in test_data:
        data.remove(d)
    return data.__class__.__name__

@cost_time
def test_dict_delete(test_data, data):
    for d in test_data:
        del data[d]
    return data.__class__.__name__

def create_data(data_size):
    data_set = {x for x in xrange(data_size)}
    data_dict = {x:None for x in xrange(data_size)}
    data_list = [x for x in xrange(data_size)]
    return data_set, data_dict, data_list

#創建隨機刪除數據集
def create_random_test_data(size, range_size):
    test_data = set()
    while(len(test_data) < size):
        test_data.add(random.randint(0, range_size))
    return test_data

#dict沒有remove方法,用del dict[key]來刪除數據,其他數據類型使用remove方法
delete_method = {list: test_delete, set: test_delete, dict: test_dict_delete}

#每次檢測1000增量大小的數據的刪除一半時間
data_size = array([x for x in xrange(1000, 20000, 1000)])
cost_result = {}
test_size = 1000
for size in data_size:
    test_data = create_random_test_data(test_size, size)
    for data in create_data(size):
        name, cost = delete_method[type(data)](test_data, data) #返回數據類型的名字和方法的執行時間
        cost_result.setdefault(name, []).append(cost)

隨機刪除一半的元素,圖形就呈指數時間(O(n2))增長了:

添加元素消耗的時間圖示如下,統計以10000為增量大小的元素個數的添加時間,都是線性增長時間,看不出有什麼差別,tuple類型不能添加新的元素,所以不做比較:
@cost_time
def test_dict_add(test_data, data):
    for d in test_data:
        data[d] = None
    return data.__class__ .__name__

@cost_time
def test_set_add(test_data, data):
    for d in test_data:
        data.add(d)
    return data.__class__ .__name__

@cost_time
def test_list_add(test_data, data):
    for d in test_data:
        data.append(d)
    return data.__class__ .__name__

#初始化數據,指定每種類型對應它添加元素的方法
def init_data():
    test_data = {
        'list': (list(), test_list_add),
        'set': (set(), test_set_add),
        'dict': (dict(), test_dict_add)
    }
    return test_data

#每次檢測10000增量大小的數據的添加時間
data_size = array([x for x in xrange(10000, 1000000, 10000)])
cost_result = {}
for size in data_size:
    test_data = [x for x in xrange(size)]
    for data_type, (data, add) in init_data().items():
        name, cost = add(test_data, data) #返回方法的執行時間
        cost_result.setdefault(data_type, []).append(cost)

plot.figure(figsize=(10, 6))
xline = data_size
for data_type, result in cost_result.items():
    yline = array(result)
    plot.plot(xline, yline, label=data_type)

plot.ylabel('Time spend')
plot.xlabel('Add times')

plot.grid()

plot.legend()
plot.show()


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 靈魂寶石(1s 128MB)soulgem 【問題描述】 “作為你們本體的靈魂,為了能夠更好的運用魔法,被賦予了既小巧又安全的外形······” 我們知道,魔法少女的生命被存放於一個稱為靈魂寶石(Soul Gem)的裝置內。而有時,當靈魂寶石與軀體的距離較遠時,魔法少女就無法控制自己的軀體了。 在傳 ...
  • 用php截取中文字元串會出現各種問題,做一簡單彙總,文中的問題暫時還未解決,有大神解決了問題歡迎指教 ...
  • 今日問題: 請問主程式輸出結果是什麼?(點擊以下“【Java每日一題】20161226”查看20161223問題解析) 題目原發佈於公眾號、簡書:【Java每日一題】20161226,【Java每日一題】20161226 註:weknow團隊近期開通並認證了分答,歡迎大家收聽,有問題也歡迎到分答來咨 ...
  • Stanford University ... ...
  • 概要:在使用storm分散式計算框架進行數據處理時,如何保證進入storm的消息的一定會被處理,且不會被重覆處理。這個時候僅僅開啟storm的ack機制並不能解決上述問題。那麼該如何設計出一個好的方案來解決上述問題? 現有架構背景:本人所在項目組的實時系統負責為XXX的實時產生的交易記錄進行處理,根 ...
  • 圖片素材: ...
  • E瀏覽器不相容的時候 註意:當在IE中時,日期會錯位一行,解決:帶有span的日期放在新聞標題的前面; 即原來新聞題目hfdfdj2014-5-30, 改成2014-5-30新聞題目hfdfdj 浮動元素先寫在前面,以免元素錯位一行 ...
  • 對象: 一切皆為對象。對象包括兩部分內容:屬性(名詞形容詞),行為(動詞)。對象和對象之間是有關係的: 派生,關聯,依賴。 類: 對同一類別的眾多對象的一種抽象。類,還是用來生成對象的一種模板,對象是類的一種具體化的表現。 面向對象的三大特性:封裝,繼承,多態。 class 類名{訪問修飾符 成員變 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...