根据条件递归地将键添加到嵌套字典

Add key to the nested dictionary recursively based on condition

我有一个多级嵌套字典,试图在该字典中有 type 键时添加一个键 required

代码:

def add_recursively(val):
   if 'type' in val and val['type'] != 'object':
      val['required'] = False
      return val
   elif 'type' in val and val['type'] == 'object' and 'object_schema' in val:
      return add_recursively(val['object_schema'])
   else:
     val['required'] = False
     return val

new_data = {}
for key, value in data.items():
    if value['type'] == 'object' and 'object_schema' in value:
       new_data[key] = add_recursively(value)
    else:
       value['required'] = False
       new_data[key] = value

输入数据:

data = {'key1': {'description': 'Configuration for OpenID', 'type': 'boolean'},
 'key2': {'default': {'enabled': True, 'logging_percent': 100},
  'object_schema': {'enabled': {'default': True, 'type': 'boolean'},
   'logging_percent': {'default': 100, 'type': 'float'},
   'nested_key': {'default': {'new_enable': True, 'new_precent': 100},
    'object_schema': {'new_enable': {'default': True, 'type': 'boolean'},
     'new_precent': {'default': 100, 'type': 'float'}},
    'type': 'object'}},
  'type': 'object'}}

输出数据:

{'key1': {'description': 'Configuration for OpenID',
  'required': False,
  'type': 'boolean'},
 'key2': {'enabled': {'default': True, 'type': 'boolean'},
  'logging_percent': {'default': 100, 'type': 'float'},
  'nested_key': {'default': {'new_enable': True, 'new_precent': 100},
   'object_schema': {'new_enable': {'default': True, 'type': 'boolean'},
    'new_precent': {'default': 100, 'type': 'float'}},
   'type': 'object'},
  'required': False}}

预期输出:

{'key1': {
  'required': False,
  'description': 'Configuration for OpenID',
  'type': 'boolean'},
 'key2': {
     'required': False,
     'enabled': {'required': False, 'default': True, 'type': 'boolean'},
    'logging_percent': {'required': False, 'default': 100, 'type': 'float'},
    'nested_key': {'default': {'new_enable': True, 'new_precent': 100},
    'object_schema': {'new_enable': {'required': False, 'default': True, 'type': 'boolean'},
      'new_precent': {'required': False, 'default': 100, 'type': 'float'}},
    'type': 'object'},
    }}

这是修改 data in-place 的一种方法,方法是向每个包含 type 键的键添加 required 键:

def add_required(d):
    if 'type' in d:
        d['required'] = False
    for v in d.values():
        if isinstance(v, dict):
            add_required(v)
add_required(data)

输出:

>>> data
{'key1': {'description': 'Configuration for OpenID',
  'type': 'boolean',
  'required': False},
 'key2': {'default': {'enabled': True, 'logging_percent': 100},
  'object_schema': {'enabled': {'default': True,
    'type': 'boolean',
    'required': False},
   'logging_percent': {'default': 100, 'type': 'float', 'required': False},
   'nested_key': {'default': {'new_enable': True, 'new_precent': 100},
    'object_schema': {'new_enable': {'default': True,
      'type': 'boolean',
      'required': False},
     'new_precent': {'default': 100, 'type': 'float', 'required': False}},
    'type': 'object',
    'required': False}},
  'type': 'object',
  'required': False}}

有多种方法可以解决这个问题(每种方法都有自己的优点和缺点),这里是创建(嵌套)对象的副本的方法。

code00.py:

#!/usr/bin/env python

import sys
from pprint import pprint as pp


data = {
    "key1": {"description": "Configuration for OpenID", "type": "boolean"},
    "key2": {
        "default": {
            "enabled": True, "logging_percent": 100,
        },
        "object_schema": {
            "enabled": {"default": True, "type": "boolean"},
            "logging_percent": {"default": 100, "type": "float"},
            "nested_key": {
                "default": {"new_enable": True, "new_precent": 100},
                "object_schema": {
                    "new_enable": {"default": True, "type": "boolean"},
                    "new_precent": {"default": 100, "type": "float"},
                },
                "type": "object",
            }
        },
        "type": "object",
    }
}


def add_rec(orig_data, new_items):
    ret = {}
    for k, v in orig_data.items():
        if k == "type":
            ret.update(new_items)
        if isinstance(v, dict):
            ret[k] = add_rec(v, new_items)
        else:
            ret[k] = v
    return ret


def main(*argv):
    new_data = add_rec(data, {"required": False})
    pp(new_data)


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出:

cfati@CFATI-5510-0:e:\Work\Dev\Whosebug\q072213710]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32

{'key1': {'description': 'Configuration for OpenID',
          'required': False,
          'type': 'boolean'},
 'key2': {'default': {'enabled': True, 'logging_percent': 100},
          'object_schema': {'enabled': {'default': True,
                                        'required': False,
                                        'type': 'boolean'},
                            'logging_percent': {'default': 100,
                                                'required': False,
                                                'type': 'float'},
                            'nested_key': {'default': {'new_enable': True,
                                                       'new_precent': 100},
                                           'object_schema': {'new_enable': {'default': True,
                                                                            'required': False,
                                                                            'type': 'boolean'},
                                                             'new_precent': {'default': 100,
                                                                             'required': False,
                                                                             'type': 'float'}},
                                           'required': False,
                                           'type': 'object'}},
          'required': False,
          'type': 'object'}}

Done.