使用字典而不是列表的开销是多少?

时间:2015-07-01 05:15:22

标签: python python-2.7

我的一个项目中有一个情况,我可以使用列表或词典,我很难选择使用哪个。

我正在分析大量商品(> 400k)。我会有(> 400k)列表或词典,我会经常使用。 (获取/设置/更新)

在我的特殊情况下,如果我根本不考虑性能,使用字典感觉就像方便而不是列表。但是,我知道我可以使用列表管理同样的事情。

我是否应该寻求可读性并使用字典或使用字典可能会增加太多的开销,从内存和时间的角度来看,这会大大降低我的性能。

我知道这个问题有点过于宽泛。但是在我做完这个决定之后我开始构建我的所有逻辑之前,我想问它。

我的情况简而言之:

我有键0,1,...,n的值。现在,密钥将始终是从0n的整数,我可以将其保存在列表中。

但是,我可以想到将来可能出现的一些情况,我需要保留一些非整数键的项目。或者不是连续的整数。

所以,问题是如果首先使用字典而不是列表不会增加大部分内存/时间成本,我首先会使用字典。但是,我不确定是否有> 400k字典与> 400k列表在性能方面有很大差异。

4 个答案:

答案 0 :(得分:5)

直接回答您的问题:词典的开销明显高于列表:

  1. 每个项目都会消耗键和值的内存,而不是列表的值。
  2. 添加或删除项目需要咨询哈希表。
  3. 尽管Python词典的设计非常精确且速度惊人,但如果你有一个可以使用直接索引的算法,你将节省空间和时间。

    然而,从您的问题和随后的讨论的声音,听起来您的需求可能会随着时间的推移而改变,并且您有一些不确定性(“但是,我可以想到将来可能出现的一些情况,我会需要保留一些非整数键的项目“

    如果是这种情况,我建议您创建自己的混合数据结构,以便随着需求的发展,您可以在隔离的地方解决存储效率,同时允许您的应用程序使用简单,可读的代码来存储和检索对象。

    例如,这是一个名为maybelist的Python3类,它是从列表派生的,但是检测是否存在非数字键,在字典中存储异常,同时为一些常见的列表操作提供映射:

    class maybelist(list):
    
        def __init__(self, *args):
            super().__init__(*args)
            self._extras = dict()
    
        def __setitem__(self, index, val):
            try:
                super().__setitem__(index, val)
                return
            except TypeError:
                # Index is not an integer, store in dict
                self._extras[index] = val
                return
            except IndexError:
                pass
            distance = index - len(self)
            if distance > 0:
                # Put 'None' in empty slots if need be
                self.extend((None,) * distance)
            self.append(val)
    
        def __getitem__(self, index):
            try:
                return super().__getitem__(index)
            except TypeError:
                return self._extras[index]
    
        def __str__(self):
            return str([item for item in self])
    
        def __len__(self):
            return super().__len__() + len(self._extras)
    
        def __iter__(self):
            for item in itertools.chain(super().__iter__(), self._extras):
                yield item
    

    所以,你可以像对待数组一样对待它,让它自动扩展:

    >>> x = maybelist()
    >>> x[0] = 'first'
    >>> x[1] = 'second'
    >>> x[10] = 'eleventh'
    >>> print(x)
    ['first', 'second', None, None, None, None, None, None, None, None, 'eleventh']
    >>> print(x[10])
    eleventh
    

    或者您可以添加带有非数字键的项目(如果它们存在):

    >>> x['unexpected'] = 'something else'
    >>> print(x['unexpected'])
    something else
    

    如果使用迭代器或您选择的其他方法访问它,该对象似乎表现得正常:

    >>> print(x)
    ['first', 'second', None, None, None, None, None, None, None, None, 'eleventh', 'unexpected']
    >>> print(len(x))
    12
    

    这只是一个示例,您需要定制这样的类以满足应用程序的需要。例如,结果对象的严格行为不像列表(例如,x[len(x)-1]不是最后一项)。但是,您的应用程序可能不需要如此严格的遵守,如果您小心并且计划得当,您可以创建一个既提供高度优化的存储又为将来留出空间以满足不断变化的数据结构需求的对象。

答案 1 :(得分:2)

dict使用的内存比list多得多。如果计算机不是很忙,可能还不足以成为一个问题。当然也有例外 - 如果它是一台每秒100个连接的Web服务器,您可能需要考虑以牺牲可读性为代价来节省内存

>>> L = range(400000)
>>> sys.getsizeof(L)
3200072   # ~3 Megabytes
>>> D = dict(zip(range(400000), range(400000)))
>>> sys.getsizeof(D)
25166104  # ~25 Megabytes

答案 2 :(得分:1)

对于你不那么明确的问题,不完全是答案,但这是我的想法:

你说

  

我正在分析大量商品(> 400k)

在这种情况下,我建议您使用生成器和/或以块的形式处理日期。

更好的选择是将您的数据(键值对)放在Redis中,并一次取出它的数据块。 Redis可以非常轻松地处理您的数据量

您可以编写一个一次处理一个块的脚本,并使用asyncio模块,您可以并行化块处理。

这样的事情:

    from concurrent import futures

    def chunk_processor(data):
        """
        Process your list data here
        """
        pass

    def parallelizer(map_func, your_data_list, n_workers=3):
        with futures.ThreadPoolExecutor(max_workers=n_workers) as executor:
            for result in executor.map(map_func, your_data_list):
                  # Do whatever with your result

    # Do the take out chunks of your data from Redis here
    chunk_of_list = get_next_chunk_from_redis()

    # Your processing starts here
    parallelizer(chunk_processor, your_data_list)

同样,可以做得更好,但我告诉你其中一个方法。

答案 3 :(得分:1)

  

列表是他们看起来的 - 一个值列表,但在字典中,你   有一个单词的'索引',并且每个单词都有一个定义。

词典是相同的,但是dict的属性与列表不同,因为它们使用映射键到值。这意味着您在时使用字典

  • 您必须根据某些标识符检索内容,例如名称,地址或任何可能是密钥的内容。
  • 你不需要按顺序排列。字典通常没有任何订单概念,所以你必须使用一个列表。
  • 您将添加和删除元素及其键。

效率约束在Stack posts Link1& Link2

  

找一本字典,因为你对未来的价值观有疑问    也没有内存约束来打扰

Reference