【问题标题】:How to assert a dict contains another dict without assertDictContainsSubset in python? [duplicate]python - 如何在python中断言一个字典包含另一个没有assertDictContainsSubset的字典? [复制]
【发布时间】:2014-01-30 05:56:21
【问题描述】:

我知道assertDictContainsSubset 可以在 python 2.7 中执行此操作,但由于某种原因,它在 python 3.2 中已被弃用。那么有没有办法断言一个字典包含另一个没有assertDictContainsSubset 的字典?

这似乎不太好:

for item in dic2:
    self.assertIn(item, dic)

还有什么好的方法吗?谢谢

【问题讨论】:

  • 没有一个解决方案像assertDictContainsSubset 一样好。我不高兴这被删除:(

标签: python python-2.7 python-3.x python-unittest


【解决方案1】:

您可以使用assertGreaterEqualassertLessEqual

users = {'id': 28027, 'email': 'chungs.lama@gmail.com', 'created_at': '2005-02-13'}
data = {"email": "chungs.lama@gmail.com"}

self.assertGreaterEqual(user.items(), data.items())
self.assertLessEqual(data.items(), user.items())  # Reversed alternative

请务必指定.items(),否则将不起作用。

【讨论】:

  • 嗯 - 尝试这样做会导致:TypeError: '>=' not supported between instances of 'dict' and 'dict'
  • @DannyStaple 与.items() 一起使用。
【解决方案2】:

John1024 的解决方案对我有用。但是,如果发生故障,它只会告诉您False,而不是告诉您哪些键不匹配。因此,我尝试通过使用其他会输出有用的失败消息的断言方法来避免已弃用的断言方法:

    expected = {}
    response_keys = set(response.data.keys())
    for key in input_dict.keys():
        self.assertIn(key, response_keys)
        expected[key] = response.data[key]
    self.assertDictEqual(input_dict, expected)

【讨论】:

    【解决方案3】:

    在 Python 3 和 Python 2.7 中,您可以在不复制任何数据的情况下创建 dict 的类似集合的“项目视图”。这使您可以使用比较运算符来测试子集关系。

    在 Python 3 中,这看起来像:

    # Test if d1 is a sub-dict of d2
    d1.items() <= d2.items()
    
    # Get items in d1 not found in d2
    difference = d1.items() - d2.items()
    

    在 Python 2.7 中,您可以使用 viewitems() 方法代替 items() 来实现相同的结果。

    在 Python 2.6 及以下版本中,最好的办法是遍历第一个 dict 中的键并检查是否包含在第二个中。

    # Test if d1 is a subset of d2
    all(k in d2 and d2[k] == d1[k] for k in d1)
    

    【讨论】:

    • 感谢您的精彩回答!对于 Python 3.x,似乎这个答案应该是公认的答案,即a.items() &lt;= b.items()。虽然我也支持@kepler 的回答(几年前确实出现过),但 2.7 之前的解释很有帮助!
    【解决方案4】:

    虽然我使用的是pytest,但我在comment 中发现了以下想法。它对我来说真的很棒,所以我认为它在这里很有用:

    assert dict1.items() <= dict2.items()
    

    适用于 Python 3 和

    assert dict1.viewitems() <= dict2.viewitems()
    

    适用于 Python 2。

    它适用于不可散列的项目,但您无法确切知道哪个项目最终失败。

    【讨论】:

      【解决方案5】:

      已接受答案的最大问题是,如果您的对象值中有不可散列的值,它就不起作用。第二件事是您没有得到有用的输出 - 测试通过或失败,但没有告诉您对象中的哪个字段不同。

      因此,简单地创建子集字典然后对其进行测试更容易。这样您就可以使用TestCase.assertDictEquals() 方法,它会在您的测试运行程序中为您提供非常有用的格式化输出,显示实际与预期之间的差异。

      我认为最令人愉快和pythonic的方法是使用简单的字典理解:

      from unittest import TestCase
      
      
      actual = {}
      expected = {}
      
      subset = {k:v for k, v in actual.items() if k in expected}
      TestCase().assertDictEqual(subset, expected)
      

      显然,如果您在属于从 TestCase 继承的子类的方法中运行测试(您几乎肯定应该这样做),那么它只是 self.assertDictEqual(subset, expected)

      【讨论】:

      • 请注意,如果expected 包含actual 不包含的密钥,则此操作不会失败。
      • 看起来会这样?如果键不是实际的,则子集不能等于预期,因为子集是从实际中的键生成的...
      【解决方案6】:

      这回答的问题比您所问的要广泛,但我在我的测试工具中使用它来查看container 字典是否包含类似于contained 字典的内容。这将检查键和值。此外,您可以使用关键字'ANYTHING' 表示您不在乎它如何匹配。

      def contains(container, contained):
          '''ensure that `contained` is present somewhere in `container`
      
          EXAMPLES:
      
          contains(
              {'a': 3, 'b': 4},
              {'a': 3}
          ) # True
      
          contains(
              {'a': [3, 4, 5]},
              {'a': 3},
          ) # True
      
          contains(
              {'a': 4, 'b': {'a':3}},
              {'a': 3}
          ) # True
      
          contains(
              {'a': 4, 'b': {'a':3, 'c': 5}},
              {'a': 3, 'c': 5}
          ) # True
      
          # if an `contained` has a list, then every item from that list must be present
          # in the corresponding `container` list
          contains(
              {'a': [{'b':1}, {'b':2}, {'b':3}], 'c':4},
              {'a': [{'b':1},{'b':2}], 'c':4},
          ) # True
      
          # You can also use the string literal 'ANYTHING' to match anything
              contains(
              {'a': [{'b':3}]},
              {'a': 'ANYTHING'},
          ) # True
      
          # You can use 'ANYTHING' as a dict key and it indicates to match the corresponding value anywhere
          # below the current point
          contains(
              {'a': [ {'x':1,'b1':{'b2':{'c':'SOMETHING'}}}]},
              {'a': {'ANYTHING': 'SOMETHING', 'x':1}},
          ) # True
      
          contains(
              {'a': [ {'x':1, 'b':'SOMETHING'}]},
              {'a': {'ANYTHING': 'SOMETHING', 'x':1}},
          ) # True
      
          contains(
              {'a': [ {'x':1,'b1':{'b2':{'c':'SOMETHING'}}}]},
              {'a': {'ANYTHING': 'SOMETHING', 'x':1}},
          ) # True
          '''
          ANYTHING = 'ANYTHING'
          if contained == ANYTHING:
              return True
      
          if container == contained:
              return True
      
          if isinstance(container, list):
              if not isinstance(contained, list):
                  contained = [contained]
              true_count = 0
              for contained_item in contained:
                  for item in container:
                      if contains(item, contained_item):
                          true_count += 1
                          break
              if true_count == len(contained):
                  return True
      
          if isinstance(contained, dict) and isinstance(container, dict):
              contained_keys = set(contained.keys())
              if ANYTHING in contained_keys:
                  contained_keys.remove(ANYTHING)
                  if not contains(container, contained[ANYTHING]):
                      return False
      
              container_keys = set(container.keys())
              if len(contained_keys - container_keys) == 0:
                  # then all the contained keys are in this container ~ recursive check
                  if all(
                      contains(container[key], contained[key])
                      for key in contained_keys
                  ):
                      return True
      
          # well, we're here, so I guess we didn't find a match yet
          if isinstance(container, dict):
              for value in container.values():
                  if contains(value, contained):
                      return True
      
          return False
      

      【讨论】:

        【解决方案7】:

        即使您的字典中有列表,这里也可以进行比较:

        superset = {'a': 1, 'b': 2}
        subset = {'a': 1}
        
        common = { key: superset[key] for key in set(superset.keys()).intersection(set(subset.keys())) }
        
        self.assertEquals(common, subset)
        

        【讨论】:

          【解决方案8】:
          >>> d1 = dict(a=1, b=2, c=3, d=4)
          >>> d2 = dict(a=1, b=2)
          >>> set(d2.items()).issubset( set(d1.items()) )
          True
          

          反之亦然:

          >>> set(d1.items()).issubset( set(d2.items()) )
          False
          

          限制:字典值必须是可散列的。

          【讨论】:

          • 这对我有用,而且很简单。谢谢,items() 等于 iteritems() 对吧?
          • iteritems 一直是 removed from python3,其中 items 返回一个迭代器。上面的代码适用于 python2 和 python3。
          • 请注意,这是否有效取决于字典的内容:如果有一些不可散列的东西,如列表,被用作值,您将无法创建 @987654326 @.
          • 这对于单元测试很好,但由于几个原因不适合生产代码。 1.它为dict项创建了全新的集合数据结构,浪费时间和内存。 2. 仅当 dict values 以及 key 是可散列的时才有效。
          • 问题是关于assertDictContainsSubset,这是一个单元测试便利功能,所以适合单元测试的解决方案是合适的
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-08
          • 1970-01-01
          • 2012-03-09
          • 1970-01-01
          • 2016-07-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多