【问题标题】:Slicing a dictionary切片字典
【发布时间】:2015-05-26 20:02:22
【问题描述】:

我有一本字典,并且想将其中的一部分传递给一个函数,该部分由一个键列表(或元组)给出。像这样:

# the dictionary
d = {1:2, 3:4, 5:6, 7:8}

# the subset of keys I'm interested in
l = (1,5)

现在,理想情况下我希望能够做到这一点:

>>> d[l]
{1:2, 5:6}

...但这不起作用,因为它会寻找一个名为(1,5) 的键。 而d[1,5] 甚至不是有效的 Python(尽管它看起来很方便)。

我知道我可以做到:

>>> dict([(key, value) for key,value in d.iteritems() if key in l])
{1: 2, 5: 6}

或者这个:

>>> dict([(key, d[key]) for key in l])

哪个更紧凑 ...但我觉得必须有一种“更好”的方式来做到这一点。我错过了更优雅的解决方案吗?

(我使用的是 Python 2.7)

【问题讨论】:

  • IMO 的理想解决方案是 d[*l],但这当然行不通。

标签: python dictionary


【解决方案1】:

在 Python 3 上,您可以使用 itertools islicedict.items() 迭代器进行切片

import itertools

d = {1: 2, 3: 4, 5: 6}

dict(itertools.islice(d.items(), 2))

{1: 2, 3: 4}

注意:此解决方案考虑特定键。它按d 的内部排序进行切片,在 Python 3.7+ 中保证是插入排序的。

【讨论】:

  • 所以......这会给我前两个元素,对吗? Python 3 中的字典是否以某种方式排序?因为否则不知道哪些元素会返回,并且它只能在连续的元素上起作用,当我真的想让它们通过键列表选择时
  • @Zak 在 python 3.6+ 上,字典是默认排序的,否则你需要使用 OrderedDict
  • 这个答案与问题无关。
  • +1 表示 python3 答案,+1 表示如果原始字典是字典的字典则有效的答案。如果我想将字典的字典视为字典的有序字典,其他答案对我不起作用。
  • @CesarCanassacan 依赖 python 3.6+ 字典来订购被认为是不好的做法,尽管它可能永远不会为了向后兼容性而改变。
【解决方案2】:

您应该遍历元组并检查键是否在字典中,而不是相反,如果您不检查键是否存在并且它不在字典中,您将得到一个键错误:

print({k:d[k] for k in l if k in d})

一些时间安排:

 {k:d[k] for k in set(d).intersection(l)}

In [22]: %%timeit                        
l = xrange(100000)
{k:d[k] for k in l}
   ....: 
100 loops, best of 3: 11.5 ms per loop

In [23]: %%timeit                        
l = xrange(100000)
{k:d[k] for k in set(d).intersection(l)}
   ....: 
10 loops, best of 3: 20.4 ms per loop

In [24]: %%timeit                        
l = xrange(100000)
l = set(l)                              
{key: d[key] for key in d.viewkeys() & l}
   ....: 
10 loops, best of 3: 24.7 ms per

In [25]: %%timeit                        

l = xrange(100000)
{k:d[k] for k in l if k in d}
   ....: 
100 loops, best of 3: 17.9 ms per loop

我看不出{k:d[k] for k in l} 不可读或不优雅,如果所有元素都在 d 中,那么它非常有效。

【讨论】:

  • 感谢您的时间! {k:d[k] for k in l} 对于有一定经验的人来说是相当可读的(比我的问题中稍微复杂一点的版本更易读),但像 d.intersect(l) 这样的东西会更好:有一本字典,一个列表,我正在做某事对他们来说,k 就不用提三遍了,既不是输入也不是操作的输出。我知道我在抱怨非常高的水平:)
  • @Zak,不用担心,我认为如果键总是在 dict 中,并且你可能给变量提供更多解释性名称,那么{k:d[k] for k in l} 是非常pythonic
  • 在这种情况下,我认为PerlRuby都提供了更“优雅”的解决方案。
【解决方案3】:

使用集合在dict.viewkeys() dictionary view 上相交:

l = {1, 5}
{key: d[key] for key in d.viewkeys() & l}

这是 Python 2 的语法,在 Python 3 中使用 d.keys()

这仍然使用循环,但至少字典理解更具可读性。使用集合交集非常有效,即使 dl 很大。

演示:

>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = {1, 5}
>>> {key: d[key] for key in d.viewkeys() & l}
{1: 2, 5: 6}

【讨论】:

  • 这有什么问题吗{key:d[key] for key in l}
  • @itzmeontv:如果您知道l 中的所有键都在d 中,那么这不是问题。但是使用d.viewkeys() & l 需要交集,即d 和集合l 中都存在的键。
  • 哦,明白了 :) {key:d[key] for key in l if key in d} 变得奇怪了吗?
  • @itzmeontv: 那么如果l 很大而d 很小呢?只需将其留给 Python 来创建交集,而不必自己循环。
【解决方案4】:

要对字典进行切片,请使用d.items() 将其转换为元组列表,然后对列表进行切片并从中创建字典。

这里。

d = {1:2, 3:4, 5:6, 7:8}

获取前 2 个项目

first_two = dict(list(d.items())[:2])

first_two

{1: 2, 3: 4}

【讨论】:

  • 我喜欢这个答案。它准确地回答了我一直在寻找的标题“Slicing a dictionary”。
  • 简单直接的答案,最好的答案。 +1
【解决方案5】:

编写一个dict 子类,它接受键列表作为“项”并返回字典的“切片”:

class SliceableDict(dict):
    default = None
    def __getitem__(self, key):
        if isinstance(key, list):   # use one return statement below
            # uses default value if a key does not exist
            return {k: self.get(k, self.default) for k in key}
            # raises KeyError if a key does not exist
            return {k: self[k] for k in key}
            # omits key if it does not exist
            return {k: self[k] for k in key if k in self}
        return dict.get(self, key)

用法:

d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d[[1, 5]]   # {1: 2, 5: 6}

或者,如果您想对此类访问使用单独的方法,您可以使用* 接受任意数量的参数:

class SliceableDict(dict):
    def slice(self, *keys):
        return {k: self[k] for k in keys}
        # or one of the others from the first example

d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d.slice(1, 5)     # {1: 2, 5: 6}
keys = 1, 5
d.slice(*keys)    # same

【讨论】:

  • 很酷的想法,虽然我宁愿添加一个额外的属性而不是弄乱现有的功能。类似d.slice(l)。实际上,我一直希望存在这样的事情。比循环更具可读性。
  • 当然,再写一个方法就完美了,或者你可以使用__call__
  • 我不会说第一个建议是 cromulent。使用您的d 的任何其他代码都将期望[ 具有通常的语义,但它们会发生变化。 slice 的例子很有趣。
  • @KenWilliams 好点。也许更好的方法是为此传递一个列表。您通常不能将列表用作字典键,因此不会丢失任何功能。
  • 编辑了第一个使用列表的建议。
【解决方案6】:

set intersectiondict comprehension可以在这里使用

# the dictionary
d = {1:2, 3:4, 5:6, 7:8}

# the subset of keys I'm interested in
l = (1,5)

>>>{key:d[key] for key in set(l) & set(d)}
{1: 2, 5: 6}

【讨论】:

    【解决方案7】:

    字典

    d = {1:2, 3:4, 5:6, 7:8}
    

    我感兴趣的键子集

    l = (1,5)
    

    回答

    {key: d[key] for key in l}
    

    【讨论】:

    • 嗨!问题中已经提到了这个答案。请详细说明您的答案,以便更有意义。
    • 如果键在字符串中怎么办?并且有“n”对?
    • @vijayraj34:只要l 是包含它们的元组、列表、迭代器或类似的键,它们的类型无关紧要。所以如果d={'b':1, 'd':2, 'f':3, 'h':8},你使用``l=('d', 'f'),同样的方法也有效。
    【解决方案8】:

    另一种选择是将字典转换为pandas Series 对象,然后定位指定的索引:

    >>> d = {1:2, 3:4, 5:6, 7:8}
    >>> l = [1,5]
    
    >>> import pandas as pd
    >>> pd.Series(d).loc[l].to_dict()
    {1: 2, 5: 6}
    

    【讨论】:

      猜你喜欢
      • 2018-12-31
      • 1970-01-01
      • 2013-03-16
      • 2017-06-03
      • 1970-01-01
      • 2015-07-26
      • 2021-04-21
      • 2021-09-14
      相关资源
      最近更新 更多