【问题标题】:Dictionary: Get list of values for list of keys字典:获取键列表的值列表
【发布时间】:2013-08-29 11:59:54
【问题描述】:

是否有一种内置/快速的方法来使用字典的键列表来获取相应项目的列表?

例如我有:

>>> mydict = {'one': 1, 'two': 2, 'three': 3}
>>> mykeys = ['three', 'one']

如何使用mykeys将字典中的对应值作为列表获取?

>>> mydict.WHAT_GOES_HERE(mykeys)
[3, 1]

【问题讨论】:

    标签: python list dictionary key


    【解决方案1】:

    列表推导式似乎是一个很好的方法:

    >>> [mydict[x] for x in mykeys]
    [3, 1]
    

    【讨论】:

    • 如果mydict 是一个函数调用(返回一个字典),那么它会多次调用该函数,对吧?
    • @endolith 会的
    • 很好的答案,谢谢!你怎么能在 2013 年 8 月 26 日 21:45 提问并在 2013 年 8 月 26 日 21:45 回答?
    • @MJimitater,他比python编译器快。
    • @MJimiter 他们answered their own question
    【解决方案2】:

    除了 list-comp 之外的其他几种方式:

    • 构建列表并在找不到密钥时抛出异常:map(mydict.__getitem__, mykeys)
    • 如果未找到密钥,则使用 None 构建列表:map(mydict.get, mykeys)

    或者,使用operator.itemgetter 可以返回一个元组:

    from operator import itemgetter
    myvalues = itemgetter(*mykeys)(mydict)
    # use `list(...)` if list is required
    

    注意:在 Python3 中,map 返回一个迭代器而不是一个列表。使用list(map(...)) 作为列表。

    【讨论】:

    【解决方案3】:

    一点速度对比:

    Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 14:10:42) [MSC v.1500 64 bit (AMD64)] on win32
    In[1]: l = [0,1,2,3,2,3,1,2,0]
    In[2]: m = {0:10, 1:11, 2:12, 3:13}
    In[3]: %timeit [m[_] for _ in l]  # list comprehension
    1000000 loops, best of 3: 762 ns per loop
    In[4]: %timeit map(lambda _: m[_], l)  # using 'map'
    1000000 loops, best of 3: 1.66 µs per loop
    In[5]: %timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
    1000000 loops, best of 3: 1.65 µs per loop
    In[6]: %timeit map(m.__getitem__, l)
    The slowest run took 4.01 times longer than the fastest. This could mean that an intermediate result is being cached 
    1000000 loops, best of 3: 853 ns per loop
    In[7]: %timeit map(m.get, l)
    1000000 loops, best of 3: 908 ns per loop
    In[33]: from operator import itemgetter
    In[34]: %timeit list(itemgetter(*l)(m))
    The slowest run took 9.26 times longer than the fastest. This could mean that an intermediate result is being cached 
    1000000 loops, best of 3: 739 ns per loop
    

    所以列表理解和 itemgetter 是最快的方法。

    更新

    对于大型随机列表和地图,我得到了一些不同的结果:

    Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 14:10:42) [MSC v.1500 64 bit (AMD64)] on win32
    In[2]: import numpy.random as nprnd
    l = nprnd.randint(1000, size=10000)
    m = dict([(_, nprnd.rand()) for _ in range(1000)])
    from operator import itemgetter
    import operator
    f = operator.itemgetter(*l)
    
    %timeit f(m)
    1000 loops, best of 3: 1.14 ms per loop
    
    %timeit list(itemgetter(*l)(m))
    1000 loops, best of 3: 1.68 ms per loop
    
    %timeit [m[_] for _ in l]  # list comprehension
    100 loops, best of 3: 2 ms per loop
    
    %timeit map(m.__getitem__, l)
    100 loops, best of 3: 2.05 ms per loop
    
    %timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
    100 loops, best of 3: 2.19 ms per loop
    
    %timeit map(m.get, l)
    100 loops, best of 3: 2.53 ms per loop
    
    %timeit map(lambda _: m[_], l)
    100 loops, best of 3: 2.9 ms per loop
    

    所以在这种情况下,明显的赢家是f = operator.itemgetter(*l); f(m),而明显的局外人:map(lambda _: m[_], l)

    Python 3.6.4 更新

    import numpy.random as nprnd
    l = nprnd.randint(1000, size=10000)
    m = dict([(_, nprnd.rand()) for _ in range(1000)])
    from operator import itemgetter
    import operator
    f = operator.itemgetter(*l)
    
    %timeit f(m)
    1.66 ms ± 74.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit list(itemgetter(*l)(m))
    2.1 ms ± 93.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit [m[_] for _ in l]  # list comprehension
    2.58 ms ± 88.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit list(map(m.__getitem__, l))
    2.36 ms ± 60.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
    2.98 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit list(map(m.get, l))
    2.7 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit list(map(lambda _: m[_], l)
    3.14 ms ± 62.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    因此,Python 3.6.4 的结果几乎相同。

    【讨论】:

      【解决方案4】:

      这里有三种方法。

      在找不到密钥时引发KeyError

      result = [mapping[k] for k in iterable]
      

      缺失键的默认值。

      result = [mapping.get(k, default_value) for k in iterable]
      

      跳过丢失的键。

      result = [mapping[k] for k in iterable if k in mapping]
      

      【讨论】:

      • found_keys = mapping.keys() & iterable 在 python 2.7 上给出TypeError: unsupported operand type(s) for &: 'list' and 'list'; `found_keys = [key for key in mapping.keys() if key in iterable] 效果最好
      【解决方案5】:

      试试这个:

      mydict = {'one': 1, 'two': 2, 'three': 3}
      mykeys = ['three', 'one','ten']
      newList=[mydict[k] for k in mykeys if k in mydict]
      print newList
      [3, 1]
      

      【讨论】:

      • "if k in mydict" 部分让它有点过于宽松 - 如果列表比字典中的键更宽但正确(更窄但不正确),则会静默失败。
      【解决方案6】:

      试试这个:

      mydict = {'one': 1, 'two': 2, 'three': 3}
      mykeys = ['three', 'one'] # if there are many keys, use a set
      
      [mydict[k] for k in mykeys]
      => [3, 1]
      

      【讨论】:

      • @PeterDeGlopper 你很困惑。首选items(),不需要额外查找,这里没有len(mydict)*len(mykeys)操作! (注意我用的是一套)
      • @ÓscarLópez 是的,您正在检查字典的每个元素。 iteritems 在您需要它们之前不会产生它们,因此它避免了构建中间列表,但您仍然为 mydict 中的每个 k 运行“k in mykeys”(命令 len(mykeys),因为它是一个列表)。与只在 mykeys 上运行的更简单的列表理解相比,完全没有必要。
      • @inspectorG4dget @PeterDeGlopper mykeys 上的成员操作是摊销常数时间,我使用的是集合,而不是列表
      • 将 OP 的列表转换为一个集合至少使它成为线性的,但它在错误的数据结构上仍然是线性的并且失去了顺序。考虑一个 10k 字典和 mykeys 中的 2 个键的情况。与用于简单列表理解的两个字典查找相比,您的解决方案进行了 10k 集成员资格测试。一般来说,假设键的数量将小于字典元素的数量似乎是安全的 - 如果不是,您的方法将省略重复的元素。
      【解决方案7】:
      new_dict = {x: v for x, v in mydict.items() if x in mykeys}
      

      【讨论】:

      • 这似乎倒退了。这个怎么样? new_dict = {x: mydict[x] for x in mykeys if x in mydict}
      • 无论如何,OP 想要一个列表,而不是一个字典。
      【解决方案8】:

      Pandas 非常优雅地做到了这一点,尽管 ofc 列表推导在技术上总是更加 Pythonic。我现在没有时间进行速度比较(我稍后会回来放入):

      import pandas as pd
      mydict = {'one': 1, 'two': 2, 'three': 3}
      mykeys = ['three', 'one']
      temp_df = pd.DataFrame().append(mydict)
      # You can export DataFrames to a number of formats, using a list here. 
      temp_df[mykeys].values[0]
      # Returns: array([ 3.,  1.])
      
      # If you want a dict then use this instead:
      # temp_df[mykeys].to_dict(orient='records')[0]
      # Returns: {'one': 1.0, 'three': 3.0}
      

      【讨论】:

        【解决方案9】:

        Python: efficient way to create a list from dict values with a given order 关闭后

        在不构建列表的情况下检索密钥:

        from __future__ import (absolute_import, division, print_function,
                                unicode_literals)
        
        import collections
        
        
        class DictListProxy(collections.Sequence):
            def __init__(self, klist, kdict, *args, **kwargs):
                super(DictListProxy, self).__init__(*args, **kwargs)
                self.klist = klist
                self.kdict = kdict
        
            def __len__(self):
                return len(self.klist)
        
            def __getitem__(self, key):
                return self.kdict[self.klist[key]]
        
        
        myDict = {'age': 'value1', 'size': 'value2', 'weigth': 'value3'}
        order_list = ['age', 'weigth', 'size']
        
        dlp = DictListProxy(order_list, myDict)
        
        print(','.join(dlp))
        print()
        print(dlp[1])
        

        输出:

        value1,value3,value2
        
        value3
        

        与列表给出的顺序匹配

        【讨论】:

          【解决方案10】:
          reduce(lambda x,y: mydict.get(y) and x.append(mydict[y]) or x, mykeys,[])
          

          如果字典中没有键。

          【讨论】:

            【解决方案11】:

            如果您发现自己经常这样做,您可能希望继承 dict 以获取键列表并返回值列表。

            >>> d = MyDict(mydict)
            >>> d[mykeys]
            [3, 1]
            

            这是一个演示实现。

            class MyDict(dict):
                def __getitem__(self, key):
                    getitem = super().__getitem__
                    if isinstance(key, list):
                        return [getitem(x) for x in key]
                    else:
                        return getitem(key)
            

            Subclassing dict well requires some more work,另外你可能想要实现.get().__setitem__(), 和.__delitem__() 等。

            【讨论】:

              猜你喜欢
              • 2020-08-06
              • 1970-01-01
              • 2019-03-27
              • 1970-01-01
              • 2022-06-21
              • 1970-01-01
              • 1970-01-01
              • 2011-11-08
              相关资源
              最近更新 更多