1-Python - deepdiff

about

DeepDiff模块常用来校验两个对象是否一致,并找出其中差异之处,它提供了:

  • DeepDiff:字典,可迭代项,字符串和其他对象的深层差异。它将递归地查找所有更改。
  • DeepSearch:在其他对象中搜索对象。
  • DeepHash:根据对象的内容对其进行哈希处理。

另外,从DeepDiff v3 版本开始,在不同的数据中有两种不同的视图:文本视图(原始)和树视图(新);而DeepHash则是 v4 版本的新功能。

版本情况
DeepDiff适用于Python 3.4、3.5、3.6、3.7,Pypy3

注意:不再支持Python 2。DeepDiff v3.3.0是支持Python 2的最新版本。

下载

pip install deepdiff==4.3.2

DeepDiff

DeepDiff可以用来校验多种类型的文件内容,如txt、json、图片等......

校验:txt文件

from deepdiff import DeepDiff
"""
a.txt的内容是: abc
b.txt的内容是: abcd
"""
f1, f2 = open('a.txt', 'r', encoding='utf-8').read(), open('b.txt', 'r', encoding='utf-8').read()
print(DeepDiff(f1, f2))  # {'values_changed': {'root': {'new_value': 'abcd', 'old_value': 'abc'}}}

校验:json文件
a.json文件内容:

{
  "title": "V2EX",
  "slogan": "way to explore",
  "description": "创意工作者们的社区",
  "domain": {
    "url": "www.v2ex.com",
    "host": "8080"
  },
  "data": [{"id":"5e4fa8531225c9423dcda9d8","author_id":"51f0f267f4963ade0e08f503","tab":"share"}, {"id":"5e16978581adfe260207a8c1","author_id":"54009f5ccd66f2eb37190485","tab":"share"}]
}

b.json文件内容:

{
  "title": "2VEX",
  "slogan": "way to explore",
  "description": "创意工作者们的社区",
  "domain": {
    "url": "www.v2ex.com",
    "host": "8080"
  },
  "data": [{"id":"5e4fa8531225c9423dcda9d8","tab":"share"}, {"id":"5e16978581adfe260207a8c1","author_id":"54009f5ccd66f2eb37190485","tab":"share_we"}]
}

示例:

import json
from deepdiff import DeepDiff
f1, f2 = json.loads(open('a.json', 'r', encoding='utf-8').read()), json.loads(open('b.json', 'r', encoding='utf-8').read())
print(DeepDiff(f1, f2))  
"""  对比结果
{
    'dictionary_item_removed': [root['data'][0]['author_id']], 
    'values_changed': {
        "root['data'][1]['tab']": {
            'new_value': 'share_we', 
            'old_value': 'share'
        }, 
        "root['title']": {
            'new_value': '2VEX', 
            'old_value': 'V2EX'
        }
    }
}
"""

校验字典

from deepdiff import DeepDiff

t1 = {1:1, 3: 3, 4: 4}
t2 = {1:1, 3: 3, 5: 5, 6: 6}
print(DeepDiff(t1, t2))  # {'dictionary_item_added': [root[5], root[6]], 'dictionary_item_removed': [root[4]]}
print(DeepDiff(t1, t2, view='tree'))  # {'dictionary_item_removed': [<root[4] t1:4, t2:not present>], 'dictionary_item_added': [<root[5] t1:not present, t2:5>, <root[6] t1:not present, t2:6>]}

默认是文本视图;使用树视图请添加view='tree'参数。

DeepSearch

DeepDiff附带了一个实用程序,可以找到要查找的项目的路径。它被称为Deep Search,它有一个类似于DeepDiff的接口。

如果你有个很深的嵌套结构对象,然后你去查找指定的元素(key和value都行)是否存在,并且返回该元素的路径,Deep Search是个好选择。

来看示例:

from deepdiff import grep

obj = {"long": "somewhere", "string": 2, 0: 0, "somewhere": "around", "a": {"b": {"c": {"d": "somewhere"}}}}
ds1 = obj | grep("somewhere")
print(ds1)  # {'matched_paths': {"root['somewhere']"}, 'matched_values': {"root['long']", "root['a']['b']['c']['d']"}}
ds2 = obj | grep("someone")
print(ds2)  # {}

如上例,指定元素存在则返回它的路径;不存在则返回一个空字典。

DeepHash

DeepHash以确定的方式根据对象的内容计算对象的哈希。这样,具有相同内容的2个对象应具有相同的哈希值。

DeepHash的主要用途是计算原本无法哈希的对象的哈希。例如,您可以使用DeepHash计算集合或字典的哈希!

DeepHash的核心是将对象确定性序列化为字符串,以便可以将其传递给哈希函数。默认情况下,它使用Murmur 3 128位哈希函数。但是您可以根据需要将另一个哈希函数传递给它。

来看示例:

from deepdiff import DeepHash

obj = {1: 2, 'a': 'b'}
# hash(obj)  # TypeError: unhashable type: 'dict'

我们试图hash上述的字典,但是报错了。
来看使用DeppHash:

from deepdiff import DeepHash

obj = {1: 2, 'a': 'b'}
print(DeepHash(obj))
"""
{
    1: 'c1800a30c736483f13615542e7096f7973631fef8ca935ee1ed9f35fb06fd44e', 
    2: '610e2bb86cee5362640bd1ab01b8a4a4559cced9dd6058376894c041629a7b69', 
    'a': '980410da9522db17c3ab8743541f192a5ab27772a6154dbc7795ee909e653a5c', 
    'b': 'd05faa460a5b4fbbfbd54286ef4e3080f5420c61daf22663163af098cd10182c', 
    '!>*id2118775877424': 'bf5478de322aa033da36bf3bcf9f0599e13a520773f50c6eb9f2487377a7929b'
}
"""

那么在这种情况下obj的哈希到底是什么?DeepHash正在计算obj和obj包含的任何其他对象的哈希。DeepHash的输出是对象ID与其哈希值的字典。为了获取obj本身的哈希,您需要使用对象(或对象的id)来获取其哈希:

from deepdiff import DeepHash

obj = {1: 2, 'a': 'b'}
hashes = DeepHash(obj)
print(DeepHash(obj))
print(hashes[obj])  # bf5478de322aa033da36bf3bcf9f0599e13a520773f50c6eb9f2487377a7929b
# 简写形式
print(DeepHash(obj)[obj])  # bf5478de322aa033da36bf3bcf9f0599e13a520773f50c6eb9f2487377a7929b

另外,DeepDiff倾向于使用Murmur3进行哈希处理。但是,您必须通过运行以下命令手动安装Murmur3:

pip install 'deepdiff[murmur]'

否则,DeepDiff将使用SHA256进行散列,这是加密散列,并且速度相当慢。
deepdiff[murmur]安装遇到问题参考:https://deepdiff.readthedocs.io/en/latest/#troubleshoot

DeepDiff在单元测试中的应用

先来个unittest框架示例:

import unittest
import requests
from deepdiff import DeepDiff


class MyCase(unittest.TestCase):
    expect = {
        "title": "V2EX",
        "slogan": "way to explore",
        "description": "创意工作者们的社区",
        "domain": "www.v2ex.com"
    }

    @classmethod
    def setUpClass(cls):
        cls.response = requests.get('https://www.v2ex.com/api/site/info.json').json()

    def test_case_01(self):
        self.assertEqual(DeepDiff(self.response, self.expect), {})

    def test_case_02(self):
        self.assertEqual(DeepDiff(self.response['title'], 'v2ex'), {})
        """
        AssertionError: {'values_changed': {'root': {'new_value': 'v2ex', 'old_value': 'V2EX'}}} != {}
        """

if __name__ == '__main__':
    unittest.main()

上述的第一个用例,由于实际的请求结果和预期值的json数据都一致,所以DeepDiff返回空字典,然后断言成功;而第二个用例我们修改预期值的title字段值,所以,DeepDiff返回了对比后的结果,与预期值不符,断言失败。
pytest框架中的使用套路是一样的:

import pytest
import requests
from deepdiff import DeepDiff

class TestCase(object):
    expect = {
        "title": "V2EX",
        "slogan": "way to explore",
        "description": "创意工作者们的社区",
        "domain": "www.v2ex.com"
    }

    def setup_class(self):
        self.response = requests.get('https://www.v2ex.com/api/site/info.json').json()

    def test_case_01(self):
        assert not DeepDiff(self.response, self.expect)

    def test_case_02(self):
        assert not DeepDiff(self.response['title'], 'v2ex')
        """
        self = <temp.TestCase object at 0x00000272F7B6FE10>

            def test_case_02(self):
        >       assert not DeepDiff(self.response['title'], 'v2ex')
        
                AssertionError: {'values_changed': {'root': {'new_value': 'v2ex', 'old_value': 'V2EX'}}} != {}
        E       AssertionError: assert not {'values_changed': {'root': {'new_value': 'v2ex', 'old_value': 'V2EX'}}}
        E        +  where {'values_changed': {'root': {'new_value': 'v2ex', 'old_value': 'V2EX'}}} = DeepDiff('V2EX', 'v2ex')
        
        temp.py:104: AssertionError
        """

if __name__ == '__main__':
    pytest.main(['-v', __file__])

see also:

pypi deepdiff | DeepDiff 4.3.0 documentation!

posted @ 2020-04-27 14:37  听雨危楼  阅读(5767)  评论(0编辑  收藏  举报