【问题标题】:One-step initialization of defaultdict that appends to list?追加到列表的defaultdict的一步初始化?
【发布时间】:2013-09-02 11:16:55
【问题描述】:

如果defaultdict 可以按照以下几行初始化会很方便

d = defaultdict(list, (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),
   ('b', 3)))

生产

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})

相反,我得到了

defaultdict(<type 'list'>, {'a': 2, 'c': 3, 'b': 3, 'd': 4})

为了得到我需要的东西,我最终不得不这样做:

d = defaultdict(list)
for x, y in (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)):
    d[x].append(y)

这比 IMO 需要的步骤多出一步,我在这里遗漏了什么吗?

【问题讨论】:

  • 如何知道在列表中使用追加?那么其他类型呢?
  • @JonClements,好点子。但是有人会认为list 是一个足够常见的用例,可以证明便利方法(可能是类方法)是合理的?
  • 这种便捷的方法难道不正是您刚刚在帖子末尾写的吗?为什么不将这三行封装在一个函数中,然后就结束了?
  • @1_CR:我想说defaultdict(list) 非常常见。但是以您建议的方式初始化该数据结构......不太好。

标签: python defaultdict


【解决方案1】:
>>> kvs = [(1,2), (2,3), (1,3)]
>>> reduce(
...   lambda d,(k,v): d[k].append(v) or d,
...   kvs,
...   defaultdict(list))
defaultdict(<type 'list'>, {1: [2, 3], 2: [3]})

【讨论】:

    【解决方案2】:

    我认为这大部分都是为了避免简单的for 循环:

    di={}
    for k,v in [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]:
        di.setdefault(k,[]).append(v)
    # di={'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}
    

    如果您的目标是一行,并且您想要我完全不能认可或支持的滥用语法,您可以使用副作用理解:

    >>> li=[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]
    >>> di={};{di.setdefault(k[0],[]).append(k[1]) for k in li}
    set([None])
    >>> di
    {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}
    

    如果你真的想走极端进入不可读的领域:

    >>> {k1:[e for _,e in v1] for k1,v1 in {k:filter(lambda x: x[0]==k,li) for k,v in li}.items()}
    {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}
    

    你不想那样做。使用 for 循环 Luke!

    【讨论】:

      【解决方案3】:

      您描述的行为与defaultdicts 的其他行为不一致。好像你想要的是FooDict 这样

      >>> f = FooDict()
      >>> f['a'] = 1
      >>> f['a'] = 2
      >>> f['a']
      [1, 2]
      

      我们可以这样做,但不能使用 defaultdict;让我们称之为 AppendDict

      import collections
      
      class AppendDict(collections.MutableMapping):
          def __init__(self, container=list, append=None, pairs=()):
              self.container = collections.defaultdict(container)
              self.append = append or list.append
              for key, value in pairs:
                  self[key] = value
      
          def __setitem__(self, key, value):
              self.append(self.container[key], value)
      
          def __getitem__(self, key): return self.container[key]
          def __delitem__(self, key): del self.container[key]
          def __iter__(self): return iter(self.container)
          def __len__(self): return len(self.container)
      

      【讨论】:

        【解决方案4】:

        您显然缺少的是defaultdictdict 的一个简单(不是特别“神奇”)的子类。第一个参数所做的只是为 missing 键提供工厂函数。当您初始化 defaultdict 时,您正在初始化 dict

        如果你想生产

        defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})
        

        您应该像初始化任何其他值为列表的dict 一样对其进行初始化:

        d = defaultdict(list, (('a', [1, 2]), ('b', [2, 3]), ('c', [3]), ('d', [4])))
        

        如果您的初始数据必须采用第二个元素始终为整数的元组形式,则只需使用for 循环即可。你称之为额外的一步;我称之为清晰明了的方法。

        【讨论】:

        • +1。顺便说一句,将其初始化为 defaultdict(list, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 也可以。
        【解决方案5】:

        排序和itertools.groupby 大有帮助:

        >>> L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)]
        >>> L.sort(key=lambda t:t[0])
        >>> d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(L, key=lambda t: t[0])])
        >>> d
        defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})
        

        为了使它更像一个单行:

        L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)]
        d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(sorted(L, key=operator.itemgetter(0)), key=lambda t: t[0])])
        

        希望对你有帮助

        【讨论】:

        • 如果 OP 不喜欢完美的 for 循环,我怀疑 itertools.groupbysort、列表组合以及 lambdaitemgetter 正在运行上诉..
        • 有趣。但请注意,sortgroupby 最终会在这里完成所有的工作。这样您就可以轻松地将groupby 的输出提供给常规的dict 而不是defaultdict
        • @1_CR:你是对的。但是,我给了你一个 defaultdict,因为你要了一个。
        • 性能怎么样,如果你把它全部放在一条线上是慢还是快?
        • @badc0re:我还没有运行timeits,但我怀疑sorted会比sort慢(malloc的时间);列表理解会比等效的 for 循环更快(几年前我timeit'd 这个,当时我对某种东西感兴趣),大概是因为解释器能够准确预测列表的大小
        猜你喜欢
        • 1970-01-01
        • 2017-08-27
        • 2020-09-24
        • 1970-01-01
        • 1970-01-01
        • 2015-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多