อัปเดตค่าพจนานุกรมที่ซ้อนกันของความลึกที่แตกต่างกัน


163

ฉันกำลังมองหาวิธีในการอัปเดตพจนานุกรม dict 1 พร้อมเนื้อหาของ dict update ที่ไม่มีการเขียนทับระดับ A

dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}

ฉันรู้ว่าการอัปเดตลบค่าในระดับ 2 เพราะเป็นการอัปเดตคีย์ระดับต่ำสุด 1

ฉันจะจัดการสิ่งนี้ได้อย่างไรเนื่องจาก dictionary1 และการอัปเดตอาจมีความยาวเท่าใด


การทำรังอยู่เสมอสามระดับลึกหรือคุณสามารถทำรังของความลึกโดยพลการหรือไม่?
ChristopheD

มันสามารถมีความลึก / ความยาวใด ๆ
jay_t

แก้ไขให้ถูกต้องถ้าฉันผิด แต่ดูเหมือนว่าทางออกที่ดีที่สุดนี้ต้องมีการนำรูปแบบการออกแบบคอมโพสิตมาใช้
Alexander McNulty

คำตอบ:


264

@ คำตอบของ FM มีความคิดทั่วไปที่ถูกต้องเช่นโซลูชันแบบเรียกซ้ำ แต่เป็นรหัสที่แปลกประหลาดและมีข้อผิดพลาดอย่างน้อยหนึ่งข้อ ฉันอยากจะแนะนำแทน:

Python 2:

import collections

def update(d, u):
    for k, v in u.iteritems():
        if isinstance(v, collections.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

Python 3:

import collections.abc

def update(d, u):
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

แสดงให้เห็นข้อผิดพลาดขึ้นเมื่อ "ปรับปรุง" มีk, vรายการที่vเป็นdictและkไม่ได้เป็นมา แต่เดิมที่สำคัญในพจนานุกรมที่ได้รับการปรับปรุง - @ FM รหัส "ข้าม" เป็นส่วนหนึ่งของการปรับปรุงนี้ (เพราะจะดำเนินการได้ในที่ว่างเปล่าใหม่dictซึ่ง ไม่ได้รับการบันทึกหรือส่งคืนทุกที่เพียงแค่หายไปเมื่อมีการโทรซ้ำเกิดซ้ำ)

การเปลี่ยนแปลงอื่น ๆ ของฉันเล็กน้อย: ไม่มีเหตุผลสำหรับif/ elseสร้างเมื่อ.getทำงานเดียวกันเร็วขึ้นและสะอาดขึ้นและisinstanceใช้กับคลาสฐานนามธรรมที่ดีที่สุด


7
+1 จับข้อผิดพลาดได้ดีมาก! ฉันคิดว่าใครบางคนจะมีวิธีที่ดีกว่าในการจัดการisinstanceทดสอบ แต่คิดว่าฉันจะแทงมัน
FMc

6
เล็ก ๆ น้อย ๆ "คุณสมบัติ" อีกสาเหตุที่ทำให้นี้เพื่อเพิ่มเมื่อคุณเช่นTypeError: 'int' object does not support item assignment. update({'k1': 1}, {'k1': {'k2': 2}})หากต้องการเปลี่ยนพฤติกรรมนี้และแทนที่จะเพิ่มความลึกของพจนานุกรมเพื่อให้มีที่ว่างสำหรับพจนานุกรมที่ลึกกว่าคุณสามารถเพิ่มelif isinstance(d, Mapping):รอบ ๆd[k] = u[k]และหลังisinstanceเงื่อนไขได้ นอกจากนี้คุณจะต้องเพิ่มelse: d = {k: u[k]}ข้อตกลงกับกรณีที่คำสั่งการอัปเดตนั้นลึกกว่าคำสั่งเดิม ยินดีที่จะแก้ไขคำตอบ แต่ไม่ต้องการสกปรกรหัสรัดกุมที่แก้ปัญหาของ OP
เตาแก๊ส

1
ทำไมต้องใช้isinstance(v, collections.Mapping)มากกว่าisinstance(v, dict)? ในกรณีที่ OP ตัดสินใจที่จะเริ่มใช้คอลเล็กชัน
แมตต์

2
@Matt Yea หรือวัตถุอื่น ๆ ที่ได้จากการทำแผนที่ (รายการคู่สิ่งต่าง ๆ ) ทำให้ฟังก์ชั่นทั่วไปกว้างขึ้นและมีโอกาสน้อยที่จะเพิกเฉยต่อวัตถุที่ได้รับจากการทำแผนที่อย่างเงียบ ๆ และทำให้พวกเขาไม่ได้รับการปรับปรุง (ข้อผิดพลาดร้ายกาจที่ OP อาจไม่เห็น / จับได้) คุณเกือบจะต้องการใช้การแมปเพื่อค้นหาประเภท dict และเบสเบสเพื่อค้นหาประเภท str
เตาแก๊ส

2
หากคุณใช้งานภายใต้ Python 3+ u.iteritems()ให้เปลี่ยนเป็นu.items()อย่างอื่นคุณจะพบกับ:AttributeError: 'dict' object has no attribute 'iteritems'
Greg K

23

พาฉันมาที่นี่เล็กน้อย แต่ต้องขอบคุณโพสต์ของ @ Alex เขาเติมเต็มช่องว่างที่ฉันหายไป อย่างไรก็ตามฉันพบปัญหาหากค่าภายใน recursive dictเกิดขึ้นเป็นlistดังนั้นฉันคิดว่าฉันจะแบ่งปันและขยายคำตอบของเขา

import collections

def update(orig_dict, new_dict):
    for key, val in new_dict.iteritems():
        if isinstance(val, collections.Mapping):
            tmp = update(orig_dict.get(key, { }), val)
            orig_dict[key] = tmp
        elif isinstance(val, list):
            orig_dict[key] = (orig_dict.get(key, []) + val)
        else:
            orig_dict[key] = new_dict[key]
    return orig_dict

3
ฉันคิดว่านี่น่าจะปลอดภัยกว่าสักหน่อย: orig_dict.get(key, []) + val.
Andy Hayden

2
เนื่องจาก dicts เปลี่ยนแปลงได้คุณจึงเปลี่ยนอินสแตนซ์ที่คุณกำลังผ่านเป็นอาร์กิวเมนต์ จากนั้นคุณไม่จำเป็นต้องส่งคืน orig_dict
gabrielhpugliese

3
ฉันคิดว่าคนส่วนใหญ่คาดหวังว่าคำจำกัดความที่จะส่งคืน dict ที่ปรับปรุงแล้วแม้ว่ามันจะมีการปรับปรุง
Kel Solaar

ตรรกะเริ่มต้นในรหัสของ onosendi คือการผนวกรายการที่อัพเดตเข้ากับรายการเดิม หากคุณต้องการอัปเดตเขียนทับรายการเดิมคุณจะต้องตั้งค่า orig_dict [key] = val
intijk

1
@gabrielhpugliese จำเป็นต้องส่งคืนต้นฉบับหากถูกเรียกด้วยพจนานุกรมตามตัวอักษรเช่น merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
EoghanM

18

@ คำตอบของอเล็กซ์เป็นสิ่งที่ดี update({'foo':0},{'foo':{'bar':1}})แต่ไม่ทำงานเมื่อเปลี่ยนองค์ประกอบเช่นจำนวนเต็มกับพจนานุกรมเช่น อัปเดตนี้เน้นที่:

import collections
def update(d, u):
    for k, v in u.iteritems():
        if isinstance(d, collections.Mapping):
            if isinstance(v, collections.Mapping):
                r = update(d.get(k, {}), v)
                d[k] = r
            else:
                d[k] = u[k]
        else:
            d = {k: u[k]}
    return d

update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})

ฉันเห็น. คุณทำให้การelifตรวจสอบวัตถุชนิดเดิมเป็นเงื่อนไข "การปิดล้อม" ที่มีการตรวจสอบทั้งค่าและคีย์ของ dict / การจับคู่นั้น ฉลาด.
เตาแก๊ส

สิ่งนี้จะไม่ทำงานหาก dict ภายในมีมากกว่าหนึ่งคีย์
Wlerin

@ Wlerin มันยังคงใช้งานได้; d จะกลายเป็นการทำแผนที่โดยจุดนั้น update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5})นี่คือกรณีทดสอบกับหลายคีย์: คุณมีตัวอย่างที่ไม่ทำสิ่งที่คุณต้องการ?
bscan

ทำไมต้องทำการทดสอบif isinstance(d, collections.Mapping)ซ้ำทุกครั้ง? ดูคำตอบของฉัน
Jérôme

13

โซลูชันเดียวกับที่ยอมรับ แต่การตั้งชื่อตัวแปรที่ชัดเจนกว่า, docstring และแก้ไขข้อบกพร่องโดยที่{}ค่าจะไม่แทนที่

import collections


def deep_update(source, overrides):
    """
    Update a nested dictionary or similar mapping.
    Modify ``source`` in place.
    """
    for key, value in overrides.iteritems():
        if isinstance(value, collections.Mapping) and value:
            returned = deep_update(source.get(key, {}), value)
            source[key] = returned
        else:
            source[key] = overrides[key]
    return source

ต่อไปนี้เป็นกรณีทดสอบบางส่วน:

def test_deep_update():
    source = {'hello1': 1}
    overrides = {'hello2': 2}
    deep_update(source, overrides)
    assert source == {'hello1': 1, 'hello2': 2}

    source = {'hello': 'to_override'}
    overrides = {'hello': 'over'}
    deep_update(source, overrides)
    assert source == {'hello': 'over'}

    source = {'hello': {'value': 'to_override', 'no_change': 1}}
    overrides = {'hello': {'value': 'over'}}
    deep_update(source, overrides)
    assert source == {'hello': {'value': 'over', 'no_change': 1}}

    source = {'hello': {'value': 'to_override', 'no_change': 1}}
    overrides = {'hello': {'value': {}}}
    deep_update(source, overrides)
    assert source == {'hello': {'value': {}, 'no_change': 1}}

    source = {'hello': {'value': {}, 'no_change': 1}}
    overrides = {'hello': {'value': 2}}
    deep_update(source, overrides)
    assert source == {'hello': {'value': 2, 'no_change': 1}}

ฟังก์ชั่นนี้สามารถใช้ได้ในปลิ้นปล้อนcharlatan.utilsแพคเกจใน


7

นี่คือการผสานพจนานุกรมแบบเรียกซ้ำไม่ได้ในกรณีที่ใครก็ตามต้องการ

ตามคำตอบของ @Alex Martelliคำตอบ

Python 2.x:

import collections
from copy import deepcopy


def merge(dict1, dict2):
    ''' Return a new dictionary by merging two dictionaries recursively. '''

    result = deepcopy(dict1)

    for key, value in dict2.iteritems():
        if isinstance(value, collections.Mapping):
            result[key] = merge(result.get(key, {}), value)
        else:
            result[key] = deepcopy(dict2[key])

    return result

Python 3.x:

import collections
from copy import deepcopy


def merge(dict1, dict2):
    ''' Return a new dictionary by merging two dictionaries recursively. '''

    result = deepcopy(dict1)

    for key, value in dict2.items():
        if isinstance(value, collections.Mapping):
            result[key] = merge(result.get(key, {}), value)
        else:
            result[key] = deepcopy(dict2[key])

    return result

6

การปรับปรุงเล็กน้อยสำหรับคำตอบของ @ Alexที่ช่วยให้สามารถอัปเดตพจนานุกรมที่มีความลึกต่างกันรวมถึงจำกัดความลึกที่การอัปเดตลงในพจนานุกรมที่ซ้อนกันดั้งเดิม (แต่ความลึกของการอัปเดตพจนานุกรมไม่ จำกัด ) มีเพียงบางกรณีเท่านั้นที่ถูกทดสอบ:

def update(d, u, depth=-1):
    """
    Recursively merge or update dict-like objects. 
    >>> update({'k1': {'k2': 2}}, {'k1': {'k2': {'k3': 3}}, 'k4': 4})
    {'k1': {'k2': {'k3': 3}}, 'k4': 4}
    """

    for k, v in u.iteritems():
        if isinstance(v, Mapping) and not depth == 0:
            r = update(d.get(k, {}), v, depth=max(depth - 1, -1))
            d[k] = r
        elif isinstance(d, Mapping):
            d[k] = u[k]
        else:
            d = {k: u[k]}
    return d

1
ขอบคุณสำหรับสิ่งนี้! พารามิเตอร์ความลึกที่ใช้กับกรณีใดบ้าง
แมตต์

@ มัดเมื่อคุณมีวัตถุ / dicts ในระดับความลึกที่คุณไม่ต้องการผสาน / อัปเดตเพียงเขียนทับด้วยวัตถุใหม่ (เช่นแทนที่ dict ด้วยสตริงหรือลอยหรือสิ่งใดก็ตามที่อยู่ลึกลงไปใน dict ของคุณ)
hobs

1
ใช้งานได้ก็ต่อเมื่อการอัปเดตนั้นลึกกว่าต้นฉบับมากที่สุด 1 ระดับ ตัวอย่างเช่นสิ่งนี้ล้มเหลว: update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})ฉันเพิ่มคำตอบที่กล่าวถึงเรื่องนี้
bscan

@ bscan จับที่ดี! ไม่เคยคิดกรณีการใช้งานที่ ฉันคิดว่าฉันควรจะเก็บลึกลงไปในกิ่งเอลฟ์ ความคิดใด ๆ
เตาแก๊ส

ทำไมต้องทำการทดสอบif isinstance(d, Mapping)ซ้ำทุกครั้ง? ดูคำตอบของฉัน (ฉันไม่แน่ใจเกี่ยวกับคุณd = {k: u[k]})
Jérôme

4

คำถามนี้เก่า แต่ฉันลงจอดที่นี่เมื่อค้นหาวิธีแก้ปัญหา "ผสานรวม" คำตอบข้างต้นเป็นแรงบันดาลใจสิ่งต่อไปนี้ ฉันสิ้นสุดการเขียนของตัวเองเพราะมีข้อบกพร่องในทุกรุ่นที่ฉันทดสอบ จุดสำคัญที่พลาดคือระดับความลึกโดยพลการของอินพุต dicts สองอันสำหรับบางคีย์ k ต้นไม้การตัดสินใจเมื่อ d [k] หรือ u [k] ไม่ใช่ dict

นอกจากนี้ยังแก้ปัญหานี้ไม่จำเป็นต้องเรียกซ้ำซึ่งเป็นสมมาตรมากขึ้นด้วยวิธีการทำงานและผลตอบแทนdict.update()None

import collections
def deep_merge(d, u):
   """Do a deep merge of one dict into another.

   This will update d with values in u, but will not delete keys in d
   not found in u at some arbitrary depth of d. That is, u is deeply
   merged into d.

   Args -
     d, u: dicts

   Note: this is destructive to d, but not u.

   Returns: None
   """
   stack = [(d,u)]
   while stack:
      d,u = stack.pop(0)
      for k,v in u.items():
         if not isinstance(v, collections.Mapping):
            # u[k] is not a dict, nothing to merge, so just set it,
            # regardless if d[k] *was* a dict
            d[k] = v
         else:
            # note: u[k] is a dict

            # get d[k], defaulting to a dict, if it doesn't previously
            # exist
            dv = d.setdefault(k, {})

            if not isinstance(dv, collections.Mapping):
               # d[k] is not a dict, so just set it to u[k],
               # overriding whatever it was
               d[k] = v
            else:
               # both d[k] and u[k] are dicts, push them on the stack
               # to merge
               stack.append((dv, v))

4

เพียงแค่ใช้python-benedict (ฉันทำมัน)แต่ก็มีmergeวิธีการอรรถประโยชน์ (deepupdate) และอื่น ๆ อีกมากมาย ใช้งานได้กับ python 2 / python 3 และผ่านการทดสอบอย่างดี

from benedict import benedict

dictionary1=benedict({'level1':{'level2':{'levelA':0,'levelB':1}}})
update={'level1':{'level2':{'levelB':10}}}
dictionary1.merge(update)
print(dictionary1)
# >> {'level1':{'level2':{'levelA':0,'levelB':10}}}

การติดตั้ง: pip install python-benedict

เอกสารประกอบ: https://github.com/fabiocaccamo/python-benedict


2

ในคำตอบเหล่านี้ผู้เขียนดูเหมือนจะเข้าใจแนวคิดของการปรับปรุงวัตถุที่เก็บไว้ในพจนานุกรมหรือแม้กระทั่งการทำซ้ำมากกว่ารายการพจนานุกรม (ตรงข้ามกับคีย์) ดังนั้นฉันจึงต้องเขียนที่ไม่ได้เก็บพจนานุกรมและการสืบค้นที่ไม่มีจุดหมาย dicts นั้นใช้เพื่อจัดเก็บ dicts อื่น ๆ หรือแบบง่าย ๆ

def update_nested_dict(d, other):
    for k, v in other.items():
        if isinstance(v, collections.Mapping):
            d_v = d.get(k)
            if isinstance(d_v, collections.Mapping):
                update_nested_dict(d_v, v)
            else:
                d[k] = v.copy()
        else:
            d[k] = v

หรือแม้แต่ใช้งานง่ายกว่ากับประเภทใดก็ได้:

def update_nested_dict(d, other):
    for k, v in other.items():
        d_v = d.get(k)
        if isinstance(v, collections.Mapping) and isinstance(d_v, collections.Mapping):
            update_nested_dict(d_v, v)
        else:
            d[k] = deepcopy(v) # or d[k] = v if you know what you're doing

2

อัปเดตเป็นคำตอบของ @Alex Martelli เพื่อแก้ไขข้อบกพร่องในรหัสของเขาเพื่อให้โซลูชันมีประสิทธิภาพยิ่งขึ้น:

def update_dict(d, u):
    for k, v in u.items():
        if isinstance(v, collections.Mapping):
            default = v.copy()
            default.clear()
            r = update_dict(d.get(k, default), v)
            d[k] = r
        else:
            d[k] = v
    return d

ที่สำคัญคือการที่เรามักจะต้องการสร้างชนิดเดียวกันที่เรียกซ้ำดังนั้นที่นี่เราใช้แต่ไม่v.copy().clear() {}และนี่เป็นประโยชน์อย่างยิ่งถ้าdictนี่คือประเภทcollections.defaultdictที่มีdefault_factorys หลายชนิด

นอกจากนี้ยังพบว่าu.iteritems()มีการเปลี่ยนแปลงไปในu.items()Python3


2

ฉันใช้โซลูชัน @Alex Martelli แนะนำ แต่ก็ล้มเหลว

TypeError 'bool' object does not support item assignment

เมื่อพจนานุกรมสองเล่มแตกต่างกันในประเภทข้อมูลในระดับหนึ่ง

ในกรณีที่อยู่ในระดับเดียวกันองค์ประกอบของพจนานุกรมdเป็นเพียงเซนต์คิตส์และเนวิสBoolในขณะที่องค์ประกอบของพจนานุกรมuยังคงเป็นพจนานุกรมการกำหนดใหม่ล้มเหลวเนื่องจากไม่สามารถกำหนดพจนานุกรมเป็นสเกลาร์ได้ (เช่นTrue[k] )

หนึ่งแก้ไขเงื่อนไขที่เพิ่มที่:

from collections import Mapping

def update_deep(d, u):
    for k, v in u.items():
        # this condition handles the problem
        if not isinstance(d, Mapping):
            d = u
        elif isinstance(v, Mapping):
            r = update_deep(d.get(k, {}), v)
            d[k] = r
        else:
            d[k] = u[k]

    return d

2

รหัสด้านล่างควรแก้ไขupdate({'k1': 1}, {'k1': {'k2': 2}})ปัญหาในคำตอบของ @Alex Martelli อย่างถูกวิธี

def deepupdate(original, update):
    """Recursively update a dict.

    Subdict's won't be overwritten but also updated.
    """
    if not isinstance(original, abc.Mapping):
        return update
    for key, value in update.items():
        if isinstance(value, abc.Mapping):
            original[key] = deepupdate(original.get(key, {}), value)
        else:
            original[key] = value
    return original

1
def update(value, nvalue):
    if not isinstance(value, dict) or not isinstance(nvalue, dict):
        return nvalue
    for k, v in nvalue.items():
        value.setdefault(k, dict())
        if isinstance(v, dict):
            v = update(value[k], v)
        value[k] = v
    return value

ใช้dictหรือcollections.Mapping


1

ฉันรู้ว่าคำถามนี้ค่อนข้างเก่า แต่ก็ยังโพสต์สิ่งที่ฉันทำเมื่อฉันต้องปรับปรุงพจนานุกรมที่ซ้อนกัน เราสามารถใช้ความจริงที่ว่า dicts ถูกส่งผ่านโดยการอ้างอิงใน python สมมติว่าเส้นทางของคีย์นั้นเป็นที่รู้จักและมีการแยกจุด Forex ถ้าเรามีข้อมูลชื่อ dict:

{
"log_config_worker": {
    "version": 1, 
    "root": {
        "handlers": [
            "queue"
        ], 
        "level": "DEBUG"
    }, 
    "disable_existing_loggers": true, 
    "handlers": {
        "queue": {
            "queue": null, 
            "class": "myclass1.QueueHandler"
        }
    }
}, 
"number_of_archived_logs": 15, 
"log_max_size": "300M", 
"cron_job_dir": "/etc/cron.hourly/", 
"logs_dir": "/var/log/patternex/", 
"log_rotate_dir": "/etc/logrotate.d/"
}

และเราต้องการอัพเดทคลาสคิวเส้นทางของคีย์จะเป็น - log_config_worker.handlers.queue.class

เราสามารถใช้ฟังก์ชั่นต่อไปนี้เพื่ออัพเดทค่า:

def get_updated_dict(obj, path, value):
    key_list = path.split(".")

    for k in key_list[:-1]:
        obj = obj[k]

    obj[key_list[-1]] = value

get_updated_dict(data, "log_config_worker.handlers.queue.class", "myclass2.QueueHandler")

นี่จะอัปเดตพจนานุกรมอย่างถูกต้อง


1

อาจเป็นไปได้ว่าคุณเจอพจนานุกรมที่ไม่ได้มาตรฐานเหมือนฉันทุกวันนี้ที่ไม่มีแอตทริบิวต์ซ้ำ - ในกรณีนี้มันง่ายที่จะตีความพจนานุกรมประเภทนี้เป็นพจนานุกรมมาตรฐาน เช่น Python 2.7:

    import collections
    def update(orig_dict, new_dict):
        for key, val in dict(new_dict).iteritems():
            if isinstance(val, collections.Mapping):
                tmp = update(orig_dict.get(key, { }), val)
                orig_dict[key] = tmp
            elif isinstance(val, list):
                orig_dict[key] = (orig_dict[key] + val)
            else:
                orig_dict[key] = new_dict[key]
        return orig_dict

    import multiprocessing
    d=multiprocessing.Manager().dict({'sample':'data'})
    u={'other': 1234}

    x=update(d, u)
    x.items()

Python 3.8:

    def update(orig_dict, new_dict):
        orig_dict=dict(orig_dict)
        for key, val in dict(new_dict).items():
            if isinstance(val, collections.abc.Mapping):
                tmp = update(orig_dict.get(key, { }), val)
                orig_dict[key] = tmp
            elif isinstance(val, list):
                orig_dict[key] = (orig_dict[key] + val)
            else:
                orig_dict[key] = new_dict[key]
        return orig_dict

    import collections
    import multiprocessing
    d=multiprocessing.Manager().dict({'sample':'data'})
    u={'other': 1234, "deeper": {'very': 'deep'}}

    x=update(d, u)
    x.items()

0

ใช่ และอีกทางออกหนึ่ง โซลูชันของฉันแตกต่างกันในคีย์ที่กำลังตรวจสอบ ในโซลูชันอื่น ๆ ทั้งหมดเราจะดูเฉพาะกุญแจเท่านั้นdict_bเท่านั้น แต่ที่นี่เรามองไปที่การรวมกันของพจนานุกรมทั้งสอง

ทำตามที่คุณต้องการ

def update_nested(dict_a, dict_b):
    set_keys = set(dict_a.keys()).union(set(dict_b.keys()))
    for k in set_keys:
        v = dict_a.get(k)
        if isinstance(v, dict):
            new_dict = dict_b.get(k, None)
            if new_dict:
                update_nested(v, new_dict)
        else:
            new_value = dict_b.get(k, None)
            if new_value:
                dict_a[k] = new_value

0

หากคุณต้องการแทนที่ "พจนานุกรมแบบซ้อนทั้งหมดด้วยอาร์เรย์" คุณสามารถใช้ตัวอย่างนี้:

มันจะแทนที่ "old_value" ใด ๆ ด้วย "new_value" มันเป็นการสร้างพจนานุกรมใหม่ในเชิงลึกขึ้นมาก่อน มันยังสามารถทำงานกับ List หรือ Str / int ที่กำหนดเป็นพารามิเตอร์อินพุตในระดับแรก

def update_values_dict(original_dict, future_dict, old_value, new_value):
    # Recursively updates values of a nested dict by performing recursive calls

    if isinstance(original_dict, Dict):
        # It's a dict
        tmp_dict = {}
        for key, value in original_dict.items():
            tmp_dict[key] = update_values_dict(value, future_dict, old_value, new_value)
        return tmp_dict
    elif isinstance(original_dict, List):
        # It's a List
        tmp_list = []
        for i in original_dict:
            tmp_list.append(update_values_dict(i, future_dict, old_value, new_value))
        return tmp_list
    else:
        # It's not a dict, maybe a int, a string, etc.
        return original_dict if original_dict != old_value else new_value

0

อีกวิธีในการใช้การเรียกซ้ำ:

def updateDict(dict1,dict2):
    keys1 = list(dict1.keys())
    keys2= list(dict2.keys())
    keys2 = [x for x in keys2 if x in keys1]
    for x in keys2:
        if (x in keys1) & (type(dict1[x]) is dict) & (type(dict2[x]) is dict):
            updateDict(dict1[x],dict2[x])
        else:
            dict1.update({x:dict2[x]})
    return(dict1)

0

ใหม่ Q วิธีการโดยพวงกุญแจ

dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}},'anotherLevel1':{'anotherLevel2':{'anotherLevelA':0,'anotherLevelB':1}}}
update={'anotherLevel1':{'anotherLevel2':1014}}
dictionary1.update(update)
print dictionary1
{'level1':{'level2':{'levelA':0,'levelB':1}},'anotherLevel1':{'anotherLevel2':1014}}

0

คุณสามารถลองใช้งานได้กับรายการและบริสุทธิ์:

def update_keys(newd, dic, mapping):
  def upsingle(d,k,v):
    if k in mapping:
      d[mapping[k]] = v
    else:
      d[k] = v
  for ekey, evalue in dic.items():
    upsingle(newd, ekey, evalue)
    if type(evalue) is dict:
      update_keys(newd, evalue, mapping)
    if type(evalue) is list:
      upsingle(newd, ekey, [update_keys({}, i, mapping) for i in evalue])
  return newd

0

ผมแนะนำให้เปลี่ยน{}โดยtype(v)()เพื่อให้ชนิดของวัตถุการเผยแพร่ของ subclass Dict ใด ๆ ที่เก็บไว้ในแต่ขาดจากu dตัวอย่างเช่นการทำเช่นนี้จะเป็นการรักษาประเภทเช่นคอลเลกชันสั่งซื้อ:

Python 2:

import collections

def update(d, u):
    for k, v in u.iteritems():
        if isinstance(v, collections.Mapping):
            d[k] = update(d.get(k, type(v)()), v)
        else:
            d[k] = v
    return d

Python 3:

import collections.abc

def update(d, u):
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            d[k] = update(d.get(k, type(v)()), v)
        else:
            d[k] = v
    return d

-1

นั่นเป็นเรื่องเล็กน้อย แต่คุณต้องการพจนานุกรมที่ซ้อนกันจริงๆหรือไม่ พจนานุกรมแบนบางครั้งอาจพอเพียง ... และดูดีขึ้นอยู่กับปัญหา:

>>> dict1 = {('level1','level2','levelA'): 0}
>>> dict1['level1','level2','levelB'] = 1
>>> update = {('level1','level2','levelB'): 10}
>>> dict1.update(update)
>>> print dict1
{('level1', 'level2', 'levelB'): 10, ('level1', 'level2', 'levelA'): 0}

5
โครงสร้างที่ซ้อนกันมาจากชุดข้อมูล JSON เข้ามาดังนั้นผมจึงอยากจะให้พวกเขาเหมือนเดิม ...
jay_t

-1

ถ้าคุณต้องการหนึ่งซับ:

{**dictionary1, **{'level1':{**dictionary1['level1'], **{'level2':{**dictionary1['level1']['level2'], **{'levelB':10}}}}}}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.