簡體   English   中英

在 Python 的嵌套字典中更新或添加新字段

[英]update or add new field in a nested dictionary in Python

我正在嘗試更新或添加一個新鍵及其在 python 字典(它是一個嵌套字典)中的值。 基本上這本字典來自 API。第一次調用時,API 以初始字典響應,如下所示:

status_from_db = {
    "management": {
        "declarations": {
            "activations": [
                {
                    "active": True,
                    "identifier": "a9c509ea-3e03-4877-846c-208e82ac2b04",
                    "server-token": "5a3d2ed9-b67e-5ebb-bbed-b4fcd79f699c",
                    "valid": "valid"
                }
            ],
            "assets": [
                {
                    "active": True,
                    "identifier": "664a311d-cead-400a-8659-2c27facd3c15",
                    "server-token": "9ca2ad23-bbcc-5651-bf7e-e861f00b92a5",
                    "valid": "valid"
                }
            ],
            "configurations": [
                {
                    "active": True,
                    "identifier": "6fb97864-e657-4600-a545-730d2e5a8a2d",
                    "server-token": "78b2239e-3617-55a4-9618-14564209cd56",
                    "valid": "valid"
                },
                {
                    "active": True,
                    "identifier": "819be2ec-fe0f-486b-87f6-b409e80053e2",
                    "server-token": "310ffc2f-50c6-591d-a730-f9d2954357b2",
                    "valid": "valid"
                },
                {
                    "active": True,
                    "identifier": "cbd9906c-046b-4586-bade-843aab5a385d",
                    "server-token": "b4975812-f2d7-5c8d-935b-239e888feed3",
                    "valid": "valid"
                }
            ],
            "management": []
        }
    },
    "mdm": {
        "app": [
            {
            "state": "prompting",
                        "identifier": "com.netflix.Netflix"
            },
            {
            "state": "prompting",
                        "identifier": "test"
            },
            {
            "state": "prompting",
                        "identifier": "blabla"
            }
        ]
    },
    "passcode": {
        "is-compliant": True,
        "is-present": True
    }
}

我正在嘗試根據新的 python 字典進行更新,其中鍵的存在是未知的。 此輸入字典也來自同一個 API,而 API 僅發送更新/添加的項目。 它不會發送所有字典以節省一些帶寬。 要更新或添加的值並不總是包含相同的鍵。 有時密鑰已經在基本字典中,需要更新,但有時它們是全新的,需要添加到基本初始字典中。

它可能看起來像這樣:

status_from_device = {
    "mdm": {
        "app": [
            {
            "removed": True,
            "identifier": "test"
            },
            {
        "state": "BLABLA",
            "identifier": "blabla"
            }
        ]

    },
    "passcode": {
        "is-present": False
    }
}

或者像那樣:

status_from_device = {
    "device": {
        "model": {
            "identifier": "my model identifier"
            }

    }
}

例如上面的字典,我想在不刪除已經存在的鍵的情況下更新嵌套字典中的正確字段,並得到類似 output 的內容。

status_from_db = {
    "management": {
        "declarations": {
            "activations": [
                {
                    "active": True,
                    "identifier": "a9c509ea-3e03-4877-846c-208e82ac2b04",
                    "server-token": "5a3d2ed9-b67e-5ebb-bbed-b4fcd79f699c",
                    "valid": "valid"
                }
            ],
            "assets": [
                {
                    "active": True,
                    "identifier": "664a311d-cead-400a-8659-2c27facd3c15",
                    "server-token": "9ca2ad23-bbcc-5651-bf7e-e861f00b92a5",
                    "valid": "valid"
                }
            ],
            "configurations": [
                {
                    "active": True,
                    "identifier": "6fb97864-e657-4600-a545-730d2e5a8a2d",
                    "server-token": "78b2239e-3617-55a4-9618-14564209cd56",
                    "valid": "valid"
                },
                {
                    "active": True,
                    "identifier": "819be2ec-fe0f-486b-87f6-b409e80053e2",
                    "server-token": "310ffc2f-50c6-591d-a730-f9d2954357b2",
                    "valid": "valid"
                },
                {
                    "active": True,
                    "identifier": "cbd9906c-046b-4586-bade-843aab5a385d",
                    "server-token": "b4975812-f2d7-5c8d-935b-239e888feed3",
                    "valid": "valid"
                }
            ],
            "management": []
        }
    },
    "mdm": {
        "app": [
            {
         "state": "prompting",
            "identifier": "com.netflix.Netflix"
            },
            {
        "state": "prompting",
            "removed": True,
            "identifier": "test"
            },
            {
        "state": "BLABLA",
            "identifier": "blabla"
            }
        ]
    },
    "passcode": {
        "is-compliant": True,
        "is-present": False
    },
    "device": {
        "model": {
           "identifier": "my model identifier"
         }
     }
}

因此,正如您所看到的,鍵“removed”被添加到正確的項目中,鍵“state”也被更新了。 我們還有 append 新的<key=device, value={"model": {"identifier": "my model identifier"}>我知道我們必須遞歸地查看所有初始字典並找到執行更新。

我有點迷失了編寫遞歸 function 以根據輸入找到要更新/添加的正確項目的正確方法。

我嘗試了幾種方法來編寫遞歸 function 但沒有成功。 我使用“set”根據基本字典 A(從 API 的第一次發送)和輸入字典 B(API 在其上發生變化時發送它)來查找需要添加和更新的鍵邊)B 中但不是 A 中的每個鍵都是新鍵 B 和 A 中的每個鍵都是具有一些更新可用的鍵。 我們不需要處理從 A 中刪除項目。

如果有人對如何解決問題有想法,那將節省我的時間。

非常感謝。

您需要同時遞歸地遍歷兩個字典。 您可以使用isinstance來確定 object 是字典、列表還是單個項目,並相應地遞歸探索 object。

下面,我寫了一個 function updated_in_depth返回一個新的、更新的字典,而不修改任何兩個字典。 但是,它不會進行深層復制,因此修改新字典或舊字典可能也會修改另一個字典。 如果這不適合您,您可以在下面的代碼中將 a a[k]替換為deepcopy(a[k]) ,並在代碼開頭使用from copy import deepcopy

我使用集合操作來區分兩個詞典中的三種類型的鍵:

keys_a, keys_b = set(a.keys()), set(b.keys())
keys_a, keys_b, keys_ab = keys_a - keys_b, keys_b - keys_a, keys_a & keys_b

這里-set.difference&set.intersection

現在keys_a包含a唯一的鍵; keys_b包含對b唯一的鍵; keys_ab包含兩個字典中都存在的鍵。

from itertools import chain

def merged_in_depth(a, b):
    if isinstance(a, dict) and isinstance(b, dict):
        keys_a, keys_b = set(a.keys()), set(b.keys())
        keys_a, keys_b, keys_ab = keys_a - keys_b, keys_b - keys_a, keys_a & keys_b 
        return dict(chain(
            ((k, a[k]) for k in keys_a),
            ((k, b[k]) for k in keys_b),
            ((k, merged_in_depth(a[k], b[k])) for k in keys_ab)
        ))
    elif isinstance(a, list) and isinstance(b, list):
        da = {x['identifier']: x for x in a}
        db = {y['identifier']: y for y in b}
        keys_a, keys_b = set(da.keys()), set(db.keys())
        keys_a, keys_b, keys_ab = keys_a - keys_b, keys_b - keys_a, keys_a & keys_b 
        return list(chain(
            (da[k] for k in keys_a),
            (db[k] for k in keys_b),
            (merged_in_depth(da[k], db[k]) for k in keys_ab)
        ))
    elif isinstance(a,dict) or isinstance(a,list) or isinstance(b,dict) or isinstance(b,list):
        raise ValueError('The two dicts have different structures!')
    else:
        return b

測試:

status_from_db = {'management': {'declarations': {'activations': [{'active': True, 'identifier': 'a9c509ea-3e03-4877-846c-208e82ac2b04', 'server-token': '5a3d2ed9-b67e-5ebb-bbed-b4fcd79f699c', 'valid': 'valid'}], 'assets': [{'active': True, 'identifier': '664a311d-cead-400a-8659-2c27facd3c15', 'server-token': '9ca2ad23-bbcc-5651-bf7e-e861f00b92a5', 'valid': 'valid'}], 'configurations': [{'active': True, 'identifier': '6fb97864-e657-4600-a545-730d2e5a8a2d', 'server-token': '78b2239e-3617-55a4-9618-14564209cd56', 'valid': 'valid'}, {'active': True, 'identifier': '819be2ec-fe0f-486b-87f6-b409e80053e2', 'server-token': '310ffc2f-50c6-591d-a730-f9d2954357b2', 'valid': 'valid'}, {'active': True, 'identifier': 'cbd9906c-046b-4586-bade-843aab5a385d', 'server-token': 'b4975812-f2d7-5c8d-935b-239e888feed3', 'valid': 'valid'}], 'management': []}}, 'mdm': {'app': [{'state': 'prompting', 'identifier': 'com.netflix.Netflix'}, {'state': 'prompting', 'identifier': 'test'}, {'state': 'prompting', 'identifier': 'blabla'}]}, 'passcode': {'is-compliant': True, 'is-present': True}}

dev1 = {'mdm': {'app': [{'removed': True, 'identifier': 'test'}, {'state': 'BLABLA', 'identifier': 'blabla'}]}, 'passcode': {'is-present': False}}

dev2 = {'device': {'model': {'identifier': 'my model identifier'}}}

print( merged_in_depth(status_from_db, dev1) )
# {'management': {'declarations': {'activations': [{'active': True, 'identifier': 'a9c509ea-3e03-4877-846c-208e82ac2b04', 'server-token': '5a3d2ed9-b67e-5ebb-bbed-b4fcd79f699c', 'valid': 'valid'}], 'assets': [{'active': True, 'identifier': '664a311d-cead-400a-8659-2c27facd3c15', 'server-token': '9ca2ad23-bbcc-5651-bf7e-e861f00b92a5', 'valid': 'valid'}], 'configurations': [{'active': True, 'identifier': '6fb97864-e657-4600-a545-730d2e5a8a2d', 'server-token': '78b2239e-3617-55a4-9618-14564209cd56', 'valid': 'valid'}, {'active': True, 'identifier': '819be2ec-fe0f-486b-87f6-b409e80053e2', 'server-token': '310ffc2f-50c6-591d-a730-f9d2954357b2', 'valid': 'valid'}, {'active': True, 'identifier': 'cbd9906c-046b-4586-bade-843aab5a385d', 'server-token': 'b4975812-f2d7-5c8d-935b-239e888feed3', 'valid': 'valid'}], 'management': []}},
#  'passcode': {'is-compliant': True, 'is-present': False},
#  'mdm': {'app': [{'state': 'prompting', 'identifier': 'com.netflix.Netflix'}, {'state': 'prompting', 'removed': True, 'identifier': 'test'}, {'state': 'BLABLA', 'identifier': 'blabla'}]}}

print( merged_in_depth(status_from_db, dev2) )
# {'passcode': {'is-compliant': True, 'is-present': True},
#  'mdm': {'app': [{'state': 'prompting', 'identifier': 'com.netflix.Netflix'}, {'state': 'prompting', 'identifier': 'test'}, {'state': 'prompting', 'identifier': 'blabla'}]},
#  'management': {'declarations': {'activations': [{'active': True, 'identifier': 'a9c509ea-3e03-4877-846c-208e82ac2b04', 'server-token': '5a3d2ed9-b67e-5ebb-bbed-b4fcd79f699c', 'valid': 'valid'}], 'assets': [{'active': True, 'identifier': '664a311d-cead-400a-8659-2c27facd3c15', 'server-token': '9ca2ad23-bbcc-5651-bf7e-e861f00b92a5', 'valid': 'valid'}], 'configurations': [{'active': True, 'identifier': '6fb97864-e657-4600-a545-730d2e5a8a2d', 'server-token': '78b2239e-3617-55a4-9618-14564209cd56', 'valid': 'valid'}, {'active': True, 'identifier': '819be2ec-fe0f-486b-87f6-b409e80053e2', 'server-token': '310ffc2f-50c6-591d-a730-f9d2954357b2', 'valid': 'valid'}, {'active': True, 'identifier': 'cbd9906c-046b-4586-bade-843aab5a385d', 'server-token': 'b4975812-f2d7-5c8d-935b-239e888feed3', 'valid': 'valid'}], 'management': []}}, 'device': {'model': {'identifier': 'my model identifier'}}}

@斯特夫

謝謝您的回答。 確實它通過使用你寫的 function 工作但你似乎假設如果元素是一個列表,一個鍵總是存在(“標識符”)如果 API 發送這種字典會發生什么。

dev3 = {"management": { "declarations": { "activations": [],"assets": [], "configurations": [],"management": [] } }}

代碼將拋出錯誤。 關於我如何修改您的 function 以使其在那種情況下工作,您有什么建議嗎?

這是一個示例 function,它接受兩個字典,status_from_db 和 status_from_device,並根據 status_from_device 中的值更新 status_from_db 中的值,而不刪除任何現有鍵:

def update_dict(status_from_db, status_from_device): for key, value in status_from_device.items(): if isinstance(value, dict): status_from_db[key] = update_dict(status_from_db.get(key, {}), value) else: status_from_db[key] = value return status_from_db

function 使用嵌套的 for 循環遍歷 status_from_device 字典的鍵和值。 對於每個鍵,function 檢查值是否為字典。 如果是,則 function 使用 status_from_db 中的當前鍵值和 status_from_device 中相應鍵的值遞歸調用自身。 如果鍵的值不是字典,function 將 status_from_device 中的值分配給 status_from_db 中的相應鍵。

您可以使用此 function 通過調用 function 並將 status_from_db 和 status_from_device 作為 arguments 傳遞來更新 status_from_db 字典:

status_from_db = update_dict(status_from_db, status_from_device)

同樣重要的是要注意這個 function 假定 status_from_device 中的鍵總是存在於 status_from_db 字典中,否則它將引發 KeyError 異常。 驗證來自 API 的鍵和值也很重要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM