递归扁平化含嵌套列表的Python字典出错,求问题定位
字典嵌套列表规范化的递归逻辑问题分析
问题背景
需要将包含嵌套列表的Python字典规范化,让每个列表元素生成一条完整的扁平化记录。
测试字典
test_dict = { 'name' : 'john', 'age' : 20, 'addresses' : [ { 'street': 'XXX', 'number': 123, 'complement' : [ 'HOUSE', 'NEAR MARKET' ] }, { 'street': 'YYY', 'number': 456, 'complement' : [ 'AP', 'NEAR PARK' ] }, ], 'phones' : [ '123456' ], 'gender' : 'MASC' }
期望输出
{'name': 'john', 'age': 20, 'street': 'XXX', 'number': 123, 'complement': 'HOUSE', 'phones': '123456', 'gender' : 'MASC'} {'name': 'john', 'age': 20, 'street': 'XXX', 'number': 123, 'complement': 'NEAR MARKET', 'phones': '123456', 'gender' : 'MASC'} {'name': 'john', 'age': 20, 'street': 'YYY', 'number': 456, 'complement': 'AP', 'phones': '123456', 'gender' : 'MASC'} {'name': 'john', 'age': 20, 'street': 'YYY', 'number': 456, 'complement': 'NEAR PARK', 'phones': '123456', 'gender' : 'MASC'}
实际输出(原代码运行结果)
{'name': 'john', 'age': 20, 'street': 'XXX', 'number': 123, 'complement': 'HOUSE'} {'name': 'john', 'age': 20, 'street': 'XXX', 'number': 123, 'complement': 'NEAR MARKET'} {'name': 'john', 'age': 20, 'street': 'YYY', 'number': 456, 'complement': 'AP'} {'name': 'john', 'age': 20, 'street': 'YYY', 'number': 456, 'complement': 'NEAR PARK'} {'name': 'john', 'age': 20, 'phones': '123456'}
原递归代码
def get_list_values(lista, dicionario, key_name, results): if len(lista) > 0: for l in lista: if isinstance(l, dict): search_values(l, dicionario.copy(), results) else: dicionario_metodo = dicionario.copy() dicionario_metodo[key_name] = l results.append(dicionario_metodo) def search_values(dicionario, test, results): for k, v in dicionario.items(): if isinstance(v, list): get_list_values(v, test, k, results ) else: test[k] = v if not any(isinstance(v, list) for v in dicionario.values()): results.append(test.copy()) return results test = {} results = [] for r in search_values(test_dict, test, results): print(r)
递归逻辑错误分析
- 字段处理中断:当遇到列表类型的字段时,直接跳转到
get_list_values处理,但没有继续处理当前字典的后续字段(比如phones、gender),导致这些字段无法被合并到列表展开后的记录中。 - 列表独立处理:代码将
phones这类顶层列表单独展开成记录,而没有和其他列表(如addresses展开后的记录)做笛卡尔积合并,生成了孤立的不完整记录。 - 上下文传递缺失:递归处理嵌套字典时,没有传递父级字典中已经收集到的所有非列表字段,逻辑上存在漏洞,仅在当前测试场景下碰巧保留了部分顶层字段。
修正后的实现
核心思路:先递归扁平化嵌套字典,分离固定字段与列表字段,再通过笛卡尔积生成所有列表元素的组合,最后将每个组合与固定字段合并为完整记录。
def flatten_dict(data, parent_key='', sep='_'): """递归扁平化嵌套字典,保留列表字段""" items = [] for k, v in data.items(): new_key = f"{parent_key}{sep}{k}" if parent_key else k if isinstance(v, dict): items.extend(flatten_dict(v, new_key, sep).items()) elif isinstance(v, list): items.append((new_key, v)) else: items.append((new_key, v)) return dict(items) def generate_records(data): """生成所有笛卡尔积组合的扁平化记录""" flat_data = flatten_dict(data) # 分离固定字段和列表字段 fixed_fields = {k: v for k, v in flat_data.items() if not isinstance(v, list)} list_fields = {k: v for k, v in flat_data.items() if isinstance(v, list)} # 计算所有列表字段的笛卡尔积 from itertools import product keys = list(list_fields.keys()) value_lists = list(list_fields.values()) records = [] for combo in product(*value_lists): record = fixed_fields.copy() record.update(dict(zip(keys, combo))) records.append(record) return records # 测试基础用例 test_dict = { 'name' : 'john', 'age' : 20, 'addresses' : [ { 'street': 'XXX', 'number': 123, 'complement' : [ 'HOUSE', 'NEAR MARKET' ] }, { 'street': 'YYY', 'number': 456, 'complement' : [ 'AP', 'NEAR PARK' ] }, ], 'phones' : [ '123456' ], 'gender' : 'MASC' } print("基础用例输出:") for record in generate_records(test_dict): print(record) # 测试更复杂的用例 complex_test_dict = { 'name' : 'john', 'age' : 20, 'addresses' : [ { 'street': 'XXX', 'number': 123, 'complement' : [ 'HOUSE', 'NEAR MARKET' ] }, { 'street': 'YYY', 'number': 456, 'complement' : [ 'AP', 'NEAR PARK' ] }, ], 'type' : { 'category': 'G123', 'products': [ 'test1', 'test2' ] }, 'phones' : [ '123456' ], 'gender' : 'MASC' } print("\n复杂用例输出:") for record in generate_records(complex_test_dict): print(record)
修正后基础用例输出
{'name': 'john', 'age': 20, 'gender': 'MASC', 'addresses_street': 'XXX', 'addresses_number': 123, 'addresses_complement': 'HOUSE', 'phones': '123456'} {'name': 'john', 'age': 20, 'gender': 'MASC', 'addresses_street': 'XXX', 'addresses_number': 123, 'addresses_complement': 'NEAR MARKET', 'phones': '123456'} {'name': 'john', 'age': 20, 'gender': 'MASC', 'addresses_street': 'YYY', 'addresses_number': 456, 'addresses_complement': 'AP', 'phones': '123456'} {'name': 'john', 'age': 20, 'gender': 'MASC', 'addresses_street': 'YYY', 'addresses_number': 456, 'addresses_complement': 'NEAR PARK', 'phones': '123456'}
复杂用例输出说明
会生成2(地址)*2(地址补充)*2(产品)*1(电话)=8条完整记录,每条记录包含所有字段:name、age、gender、addresses_street、addresses_number、addresses_complement、type_category、type_products、phones。
内容的提问来源于stack exchange,提问作者CooperativistKid
相关产品推荐
相关产品推荐

