【问题标题】:python dict update diffpython dict更新差异
【发布时间】:2010-10-17 10:41:41
【问题描述】:

python 是否有任何内置功能来通知字典更新时哪些字典元素发生了变化?例如,我正在寻找这样的一些功能:

>>> a = {'a':'hamburger', 'b':'fries', 'c':'coke'}
>>> b = {'b':'fries', 'c':'pepsi', 'd':'ice cream'}
>>> a.diff(b)
{'c':'pepsi', 'd':'ice cream'}
>>> a.update(b)
>>> a
{'a':'hamburger', 'b':'fries', 'c':'pepsi', 'd':'ice cream'}

我希望得到一个更改值的字典,如 a.diff(b) 的结果所示

【问题讨论】:

  • 是您正在寻找的 a.diff(),还是您正在寻找将返回值已更改的 a 中的键的 a.update(b) 版本?跨度>
  • 我正在寻找更改值的字典。

标签: python diff dictionary


【解决方案1】:

不是内置的,但您可以迭代 dict 的键并进行比较。不过可能会很慢。

更好的解决方案可能是构建更复杂的数据结构并使用字典作为底层表示。

【讨论】:

    【解决方案2】:

    不,它没有。但是写个字典diff函数并不难:

    def diff(a, b):
      diff = {}
      for key in b.keys():
        if (not a.has_key(key)) or (a.has_key(key) and a[key] != b[key]):
          diff[key] = b[key]
      return diff
    

    【讨论】:

      【解决方案3】:

      不,但您可以将 dict 子类化以提供更改通知。

      class ObservableDict( dict ):
          def __init__( self, *args, **kw ):
              self.observers= []
              super( ObservableDict, self ).__init__( *args, **kw )
          def observe( self, observer ):
              self.observers.append( observer )
          def __setitem__( self, key, value ):
              for o in self.observers:
                  o.notify( self, key, self[key], value )
              super( ObservableDict, self ).__setitem__( key, value )
          def update( self, anotherDict ):
              for k in anotherDict:
                  self[k]= anotherDict[k]
      
      class Watcher( object ):
          def notify( self, observable, key, old, new ):
              print "Change to ", observable, "at", key
      
      w= Watcher()
      a= ObservableDict( {'a':'hamburger', 'b':'fries', 'c':'coke'} )
      a.observe( w )
      b = {'b':'fries', 'c':'pepsi'}
      a.update( b )
      

      请注意,此处定义的超类 Watcher 不会检查是否存在“真正的”变化;它只是指出发生了变化。

      【讨论】:

      • 为什么 ObservableDict 会覆盖更新方法?内置的更新方法不调用setitem吗?
      • 在我的快速测试中似乎没有。也许我错过了什么。
      【解决方案4】:
      def diff_update(dict_to_update, updater):
          changes=dict((k,updater[k]) for k in filter(lambda k:(k not in dict_to_update or updater[k] != dict_to_update[k]), updater.iterkeys()))
          dict_to_update.update(updater)
          return changes
      
      a = {'a':'hamburger', 'b':'fries', 'c':'coke'}
      b = {'b':'fries', 'c':'pepsi'}
      >>> print diff_update(a, b)
      {'c': 'pepsi'}
      >>> print a
      {'a': 'hamburger', 'c': 'pepsi', 'b': 'fries'}
      

      【讨论】:

      • 我喜欢这个函数如何处理更新和返回更改。但是,如果我必须在 6 个月内回到这个问题来修复错误,我可能希望我使用简单的 for 循环而不是 lambda。
      【解决方案5】:

      一个简单的差异函数很容易编写。根据您需要它的频率,它可能比 S.Lott 更优雅的 ObservableDict 更快。

      def dict_diff(a, b):
          """Return differences from dictionaries a to b.
      
          Return a tuple of three dicts: (removed, added, changed).
          'removed' has all keys and values removed from a. 'added' has
          all keys and values that were added to b. 'changed' has all
          keys and their values in b that are different from the corresponding
          key in a.
      
          """
      
          removed = dict()
          added = dict()
          changed = dict()
      
          for key, value in a.iteritems():
              if key not in b:
                  removed[key] = value
              elif b[key] != value:
                  changed[key] = b[key]
          for key, value in b.iteritems():
              if key not in a:
                  added[key] = value
          return removed, added, changed
      
      if __name__ == "__main__":
          print dict_diff({'foo': 1, 'bar': 2, 'yo': 4 }, 
                          {'foo': 0, 'foobar': 3, 'yo': 4 })
      

      【讨论】:

      • 事后差异比较必须比每次更新的差异日志慢。在更新发生时收集差异不需要事后循环。
      • 在进行更新时跟踪更改是有成本的。如果您只需要很少的差异,那么很少计算它可能比在每次更改期间累积成本更便宜。
      【解决方案6】:

      一年后

      我喜欢以下解决方案:

      >>> def dictdiff(d1, d2):                                              
              return dict(set(d2.iteritems()) - set(d1.iteritems()))
      ... 
      >>> a = {'a':'hamburger', 'b':'fries', 'c':'coke'}
      >>> b = {'b':'fries', 'c':'pepsi', 'd':'ice cream'}
      >>> dictdiff(a, b)
      {'c': 'pepsi', 'd': 'ice cream'}
      

      【讨论】:

      • 适用于带有简单对象的平面字典。有一个递归到嵌套字典的实现会很好......
      • @DaveRawks 他们在这里讨论递归更改link
      猜你喜欢
      • 1970-01-01
      • 2011-08-30
      • 1970-01-01
      • 2015-07-21
      • 2020-03-01
      • 2018-03-31
      • 2017-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多