最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】Python中实现dict的递归的合并更新

Python crifan 6111浏览 0评论

折腾:

【记录】用VSCode开发和调试Python

期间,需要去处理:

把a和b两个dict合并:

book_common.json

<code>{
  "title": "Gitbook的书名",
  "description": "gitbook书的描述",
  "author": "Crifan Li &lt;[email protected]&gt;",
  "language": "zh-hans",
  "gitbook": "3.2.3",
  "root": "./src",
  "links": {
    "sidebar": {
      "主页": "https://www.crifan.com"
    }
  },
  "plugins": [
    "theme-comscore",
    "-lunr",
    "-search",
    "search-plus",
    "disqus",
    "-highlight",
    "prism",
    "prism-themes",
    "github-buttons",
    "splitter",
    "-sharing",
    "sharing-plus",
    "tbfed-pagefooter",
    "expandable-chapters-small",
    "ga",
    "donate",
    "sitemap-general",
    "copy-code-button",
    "-alerts",
    "-bootstrap-callout",
    "callouts",
    "toolbar-button"
  ],
  "pluginsConfig": {
    "callouts": {
      "showTypeInHeader": false
        },
    "theme-default": {
        "showLevel": true
    },
    "disqus": {
      "shortName": "crifan"
    },
    "prism": {
      "css": [
        "prism-themes/themes/prism-atom-dark.css"
      ]
    },
    "github-buttons": {
      "buttons": [
        {
          "user": "crifan",
          "repo": "gitbook_name",
          "type": "star",
          "count": true,
          "size": "small"
        }, {
          "user": "crifan",
          "type": "follow",
          "width": "120",
          "count": false,
          "size": "small"
        }
      ]
    },
    "sharing": {
      "douban": false,
      "facebook": true,
      "google": false,
      "hatenaBookmark": false,
      "instapaper": false,
      "line": false,
      "linkedin": false,
      "messenger": false,
      "pocket": false,
      "qq": true,
      "qzone": false,
      "stumbleupon": false,
      "twitter": true,
      "viber": false,
      "vk": false,
      "weibo": true,
      "whatsapp": false,
      "all": [
        "douban",
        "facebook",
        "google",
        "instapaper",
        "line",
        "linkedin",
        "messenger",
        "pocket",
        "qq",
        "qzone",
        "stumbleupon",
        "twitter",
        "viber",
        "vk",
        "weibo",
        "whatsapp"
      ]
    },
    "tbfed-pagefooter": {
      "copyright": "crifan.com,使用&lt;a href='https://creativecommons.org/licenses/by-sa/4.0/deed.zh'&gt;知识署名-相同方式共享4.0协议&lt;/a&gt;发布",
      "modify_label": "该文件修订时间:",
      "modify_format": "YYYY-MM-DD HH:mm:ss"
    },
    "ga": {
      "token": "UA-28297199-1"
    },
    "donate": {
      "wechat": "https://www.crifan.com/files/res/crifan_com/crifan_wechat_pay.jpg",
      "alipay": "https://www.crifan.com/files/res/crifan_com/crifan_alipay_pay.jpg",
      "title": "",
      "button": "打赏",
      "alipayText": "支付宝打赏给Crifan",
      "wechatText": "微信打赏给Crifan"
    },
    "sitemap-general": {
      "prefix": "https://book.crifan.com/gitbook/gitbook_name/website/"
    },
    "toolbar-button": {
      "icon": "fa-file-pdf-o",
      "label": "下载PDF",
      "url": "http://book.crifan.com/books/gitbook_name/pdf/gitbook_name.pdf"
    }
  }
}
</code>

book_current.json

<code>{
  "title": "有道云笔记和云协作使用总结",
  "description": "总结之前使用过有道云笔记和有道云协作的心得供参考",
  "pluginsConfig": {
    "github-buttons": {
      "buttons": [
        {
          "repo": "youdao_note_summary"
        }
      ]
    },
    "sitemap-general": {
      "prefix": "https://book.crifan.com/gitbook/youdao_note_summary/website/"
    },
    "toolbar-button": {
      "url": "http://book.crifan.com/books/youdao_note_summary/pdf/youdao_note_summary.pdf"
    }
  }
}
</code>

希望合并后是:

<code>{
  "title": "有道云笔记和云协作使用总结",
  "description": "总结之前使用过有道云笔记和有道云协作的心得供参考",
  "author": "Crifan Li &lt;[email protected]&gt;",
  "language": "zh-hans",
  "gitbook": "3.2.3",
  "root": "./src",
  "links": {
    "sidebar": {
      "主页": "https://www.crifan.com"
    }
  },
  "plugins": [
    "theme-comscore",
    "-lunr",
    "-search",
    "search-plus",
    "disqus",
    "-highlight",
    "prism",
    "prism-themes",
    "github-buttons",
    "splitter",
    "-sharing",
    "sharing-plus",
    "tbfed-pagefooter",
    "expandable-chapters-small",
    "ga",
    "donate",
    "sitemap-general",
    "copy-code-button",
    "-alerts",
    "-bootstrap-callout",
    "callouts",
    "toolbar-button"
  ],
  "pluginsConfig": {
    "callouts": {
      "showTypeInHeader": false
        },
    "theme-default": {
        "showLevel": true
    },
    "disqus": {
      "shortName": "crifan"
    },
    "prism": {
      "css": [
        "prism-themes/themes/prism-atom-dark.css"
      ]
    },
    "github-buttons": {
      "buttons": [
        {
          "user": "crifan",
          "repo": "youdao_note_summary",
          "type": "star",
          "count": true,
          "size": "small"
        }, {
          "user": "crifan",
          "type": "follow",
          "width": "120",
          "count": false,
          "size": "small"
        }
      ]
    },
    "sharing": {
      "douban": false,
      "facebook": true,
      "google": false,
      "hatenaBookmark": false,
      "instapaper": false,
      "line": false,
      "linkedin": false,
      "messenger": false,
      "pocket": false,
      "qq": true,
      "qzone": false,
      "stumbleupon": false,
      "twitter": true,
      "viber": false,
      "vk": false,
      "weibo": true,
      "whatsapp": false,
      "all": [
        "douban",
        "facebook",
        "google",
        "instapaper",
        "line",
        "linkedin",
        "messenger",
        "pocket",
        "qq",
        "qzone",
        "stumbleupon",
        "twitter",
        "viber",
        "vk",
        "weibo",
        "whatsapp"
      ]
    },
    "tbfed-pagefooter": {
      "copyright": "crifan.com,使用&lt;a href='https://creativecommons.org/licenses/by-sa/4.0/deed.zh'&gt;知识署名-相同方式共享4.0协议&lt;/a&gt;发布",
      "modify_label": "该文件修订时间:",
      "modify_format": "YYYY-MM-DD HH:mm:ss"
    },
    "ga": {
      "token": "UA-28297199-1"
    },
    "donate": {
      "wechat": "https://www.crifan.com/files/res/crifan_com/crifan_wechat_pay.jpg",
      "alipay": "https://www.crifan.com/files/res/crifan_com/crifan_alipay_pay.jpg",
      "title": "",
      "button": "打赏",
      "alipayText": "支付宝打赏给Crifan",
      "wechatText": "微信打赏给Crifan"
    },
    "sitemap-general": {
      "prefix": "https://book.crifan.com/gitbook/youdao_note_summary/website/"
    },
    "toolbar-button": {
      "icon": "fa-file-pdf-o",
      "label": "下载PDF",
      "url": "http://book.crifan.com/books/youdao_note_summary/pdf/youdao_note_summary.pdf"
    }
  }
}
</code>

python json merge

python dict merge

How to merge two json string in Python? – Stack Overflow

jsonmerge · PyPI

python – How to merge two dictionaries in a single expression? – Stack Overflow

去试试效果

结果:

<code>bookJson = {**templateJson, **currentJson}
pprint(bookJson)
</code>

合并后是:

<code>{
  'root': './src',
  'title': '有道云笔记和云协作使用总结',
  'author': 'Crifan Li &lt;[email protected]&gt;',
  'description': '总结之前使用过有道云笔记和有道云协作的心得供参考',
  'gitbook': '3.2.3',
  'language': 'zh-hans',
  'links': {
    'sidebar': {
      '主页': 'https://www.crifan.com'
    }
  },
  'plugins': [
    'theme-comscore',
    '-lunr',
    '-search',
    'search-plus',
    'disqus',
    '-highlight',
    'prism',
    'prism-themes',
    'github-buttons',
    'splitter',
    '-sharing',
    'sharing-plus',
    'tbfed-pagefooter',
    'expandable-chapters-small',
    'ga',
    'donate',
    'sitemap-general',
    'copy-code-button',
    '-alerts',
    '-bootstrap-callout',
    'callouts',
    'toolbar-button'
  ],
  'pluginsConfig': {
    'github-buttons': {
      'buttons': [{
        'repo': 'youdao_note_summary'
      }]
    },
    'sitemap-general': {
      'prefix': 'https://book.crifan.com/gitbook/youdao_note_summary/website/'
    },
    'toolbar-button': {
      'url': 'http://book.crifan.com/books/youdao_note_summary/pdf/youdao_note_summary.pdf'
    }
  }
}
</code>

其中基本的字段,比如title和description,是合并update了的

但是pluginsConfig的部分,只有后者,没有前面的值了。

试试别的方案

<code>bookJson = templateJson.copy()
bookJson.update(currentJson)
pprint(bookJson)
</code>

问题依旧:

<code>bookJson = dict(templateJson, **currentJson)
</code>

问题依旧。

<code>from collections import ChainMap
bookJson = ChainMap({}, currentJson, templateJson)
print("type(bookJson)=", type(bookJson))
</code>

问题依旧。

<code>def recursiveUpdateDict(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            recursiveUpdateDict(value, update[key]) 
    return update

bookJson = recursiveUpdateDict(templateJson, currentJson)
</code>

但是出错:

<code>    for key, value in original.iteritems():
AttributeError: 'dict' object has no attribute 'iteritems'
</code>

AttributeError: ‘dict’ object has no attribute ‘iteritems’

AttributeError: ‘dict’ object has no attribute ‘iteritems’ – CSDN博客

Python3下AttributeError: ‘dict’ object has no attribute ‘iteritems’的问题分析 – CSDN博客

改为:

<code>def recursiveUpdateDict(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    # for key, value in original.iteritems():
    for key, value in original.items():
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            recursiveUpdateDict(value, update[key]) 
    return update

bookJson = recursiveUpdateDict(templateJson, currentJson)
</code>

结果:

好像是可以的。

再去升级代码,支持Python2和Python3:

经过优化后,完整代码是:

<code>from pprint import pprint
import sys

templateJson = {}
currentJson = {}

。。。

def recursiveMergeDict(originalDict, toMergeDict):
    """
    Recursively update a dict.
    Sub dict's won't be overwritten but also updated.
    """
    originalDictItems = None
    if (sys.version_info[0] == 2): # is python 2
      originalDictItems = originalDict.iteritems()
    else:
      originalDictItems = originalDict.items()
    
    mergedDict = toMergeDict.copy()

    for key, value in originalDictItems:
      if key not in mergedDict:
          mergedDict[key] = value
      elif isinstance(value, dict):
          recursiveMergeDict(value, mergedDict[key])

    return mergedDict

bookJson = recursiveMergeDict(templateJson, currentJson)

pprint("-"*80)

pprint(bookJson)
pprint("-"*80)
pprint(currentJson)

</code>

效果:

可见,merge后,a和b中的b,也是没有被修改的,而是返回a和b合并后的结果c的。

【后记1】

后来发现上述写法有问题:

传入a和b,结果b会被修改

而上面的写法,没法起到递归的效果

【已解决】python中的dict被copy后仍会被修改

【后记2】

折腾了:

【已解决】python中把dict的json输出到文件且带缩进和不要unicode的\uxxxx

后,输出的结果是:

<code>{
  "description": "总结之前使用过有道云笔记和有道云协作的心得供参考", 
  "links": {
    "sidebar": {
      "主页": "https://www.crifan.com"
    }
  }, 
  "author": "Crifan Li &lt;[email protected]&gt;", 
  "title": "有道云笔记和云协作使用总结", 
  "gitbook": "3.2.3", 
  "language": "zh-hans", 
  "plugins": [
    "theme-comscore", 
    "-lunr", 
    "-search", 
    "search-plus", 
    "disqus", 
    "-highlight", 
    "prism", 
    "prism-themes", 
    "github-buttons", 
    "splitter", 
    "-sharing", 
    "sharing-plus", 
    "tbfed-pagefooter", 
    "expandable-chapters-small", 
    "ga", 
    "donate", 
    "sitemap-general", 
    "copy-code-button", 
    "-alerts", 
    "-bootstrap-callout", 
    "callouts", 
    "toolbar-button"
  ], 
  "pluginsConfig": {
    "toolbar-button": {
      "url": "http://book.crifan.com/books/youdao_note_summary/pdf/youdao_note_summary.pdf", 
      "icon": "fa-file-pdf-o", 
      "label": "下载PDF"
    }, 
    "sitemap-general": {
      "prefix": "https://book.crifan.com/gitbook/youdao_note_summary/website/"
    }, 
    "sharing": {
      "qq": true, 
      "douban": false, 
      "all": [
        "douban", 
        "facebook", 
        "google", 
        "instapaper", 
        "line", 
        "linkedin", 
        "messenger", 
        "pocket", 
        "qq", 
        "qzone", 
        "stumbleupon", 
        "twitter", 
        "viber", 
        "vk", 
        "weibo", 
        "whatsapp"
      ], 
      "google": false, 
      "stumbleupon": false, 
      "hatenaBookmark": false, 
      "twitter": true, 
      "vk": false, 
      "linkedin": false, 
      "instapaper": false, 
      "pocket": false, 
      "weibo": true, 
      "viber": false, 
      "facebook": true, 
      "qzone": false, 
      "messenger": false, 
      "line": false, 
      "whatsapp": false
    }, 
    "tbfed-pagefooter": {
      "modify_format": "YYYY-MM-DD HH:mm:ss", 
      "modify_label": "该文件修订时间:", 
      "copyright": "crifan.com,使用&lt;a href='https://creativecommons.org/licenses/by-sa/4.0/deed.zh'&gt;知识署名-相同方式共享4.0协议&lt;/a&gt;发布"
    }, 
    "github-buttons": {
      "buttons": [
        {
          "repo": "youdao_note_summary"
        }
      ]
    }, 
    "disqus": {
      "shortName": "crifan"
    }, 
    "prism": {
      "css": [
        "prism-themes/themes/prism-atom-dark.css"
      ]
    }, 
    "ga": {
      "token": "UA-28297199-1"
    }, 
    "theme-default": {
      "showLevel": true
    }, 
    "donate": {
      "alipay": "https://www.crifan.com/files/res/crifan_com/crifan_alipay_pay.jpg", 
      "title": "", 
      "alipayText": "支付宝打赏给Crifan", 
      "button": "打赏", 
      "wechatText": "微信打赏给Crifan", 
      "wechat": "https://www.crifan.com/files/res/crifan_com/crifan_wechat_pay.jpg"
    }, 
    "callouts": {
      "showTypeInHeader": false
    }
  }, 
  "root": "./src"
}
</code>

其中大部分的json的内容,是合并对了。

但是部分内容,并没有合并更新:

对于:

<code>    "github-buttons": {
      "buttons": [
        {
          "user": "crifan",
          "repo": "gitbook_name",
          "type": "star",
          "count": true,
          "size": "small"
        }, {
          "user": "crifan",
          "type": "follow",
          "width": "120",
          "count": false,
          "size": "small"
        }
      ]
    },
</code>

和:

<code>    "github-buttons": {
      "buttons": [
        {
          "repo": "youdao_note_summary"
        }
      ]
    },
</code>

结果是:

<code>    "github-buttons": {
      "buttons": [
        {
          "repo": "youdao_note_summary"
        }
      ]
    }, 
</code>

其实希望的是

<code>    "github-buttons": {
      "buttons": [
        {
          "user": "crifan",
          "repo": "youdao_note_summary",
          "type": "star",
          "count": true,
          "size": "small"
        }, {
          "user": "crifan",
          "type": "follow",
          "width": "120",
          "count": false,
          "size": "small"
        }
      ]
    },
</code>

所以前面的合并算法还是有问题。

去优化

【总结】

最后用代码:

<code>def recursiveMergeDict(aDict, bDict):
    """
    Recursively merge dict a to b, return merged dict b
    Note: Sub dict's won't be overwritten but also updated/merged
    """
    aDictItems = None
    if (sys.version_info[0] == 2): # is python 2
      aDictItems = aDict.iteritems()
    else: # is python 3
      aDictItems = aDict.items()

    for aKey, aValue in aDictItems:
      print("------ [%s]=%s" % (aKey, aValue))
      if aKey not in bDict:
        bDict[aKey] = aValue
      else:
        bValue = bDict[aKey]
        print("aValue=%s" % aValue)
        print("bValue=%s" % bValue)
        if isinstance(aValue, dict):
          recursiveMergeDict(aValue, bValue)
        elif isinstance(aValue, list):
          aValueListLen = len(aValue)
          bValueListLen = len(bValue)
          bValueListMaxIdx = bValueListLen - 1
          for aListIdx in range(aValueListLen):
            print("---[%d]" % aListIdx)
            aListItem = aValue[aListIdx]
            print("aListItem=%s" % aListItem)
            if aListIdx &lt;= bValueListMaxIdx:
              bListItem = bValue[aListIdx]
              print("bListItem=%s" % bListItem)
              recursiveMergeDict(aListItem, bListItem)
            else:
              # recursiveMergeDict(aListItem, aListItem)
              print("bDict=%s" % bDict)
              print("aKey=%s" % aKey)
              print("aListItem=%s" % aListItem)
              bDict[aKey].append(aListItem)

    return bDict

bookJson = recursiveMergeDict(templateJson, copy.deepcopy(currentJson))

pprint("-a"*40)
pprint(templateJson)
pprint("-b"*40)
pprint(currentJson)
pprint("-c"*40)
pprint(bookJson)
</code>

输出:

<code>'-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a'
{u'pluginsConfig': {u'github-buttons': {u'buttons': [{u'count': True,
                                                      u'repo': u'gitbook_name',
                                                      u'size': u'small',
                                                      u'type': u'star',
                                                      u'user': u'crifan'},
                                                     {u'count': False,
                                                      u'size': u'small',
                                                      u'type': u'follow',
                                                      u'user': u'crifan',
                                                      u'width': u'120'},
                                                     {u'key1': u'string1',
                                                      u'key2': 999,
                                                      u'key3': True}]}}}
'-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b'
{u'pluginsConfig': {u'github-buttons': {u'buttons': [{u'repo': u'youdao_note_summary'}]}}}
'-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c'
{u'pluginsConfig': {u'github-buttons': {u'buttons': [{u'count': True,
                                                      u'repo': u'youdao_note_summary',
                                                      u'size': u'small',
                                                      u'type': u'star',
                                                      u'user': u'crifan'},
                                                     {u'count': False,
                                                      u'size': u'small',
                                                      u'type': u'follow',
                                                      u'user': u'crifan',
                                                      u'width': u'120'},
                                                     {u'key1': u'string1',
                                                      u'key2': 999,
                                                      u'key3': True}]}}}
</code>

实现了希望看到的效果:

合并a和b的dict

  • 如果包含子dict,则递归去合并

  • 如果包含list,则针对每个item去合并

    • 如果a的list比b多,则保留a多出的部分

转载请注明:在路上 » 【已解决】Python中实现dict的递归的合并更新

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.176 seconds, using 22.13MB memory