在嵌套Python dict和record中搜索;路径“;

在嵌套Python dict和record中搜索;路径“;,python,dictionary,yield,Python,Dictionary,Yield,借助于此,我正试图找到一个函数,该函数在嵌套Python dict中搜索一个键,并记录每个匹配的“路径”。我的函数(见下文)似乎正常工作,但无法将结果保存在列表中(见代码输出)。我很确定困难在于yield命令,但我还没有弄明白 o={ 'dict1': { 'dict11': { 'entry11_1':1, 'entry11_2':2, }, 'dict12': { 'entry12_1':12, 'entry12_2

借助于此,我正试图找到一个函数,该函数在嵌套Python dict中搜索一个键,并记录每个匹配的“路径”。我的函数(见下文)似乎正常工作,但无法将结果保存在列表中(见代码输出)。我很确定困难在于
yield
命令,但我还没有弄明白

o={
  'dict1': {
    'dict11': {
      'entry11_1':1,
      'entry11_2':2,
    },
    'dict12': {
      'entry12_1':12,
      'entry12_2':22,
    },
  },
  'dict2': {
    'dict21': {
      'entry21_1':21,
    }
  },
}


curr_pos=[]
def gen_dict_extract(key, var):
  global curr_pos
  if hasattr(var,'iteritems'):
    for k, v in var.iteritems():
      #print curr_pos
      if k == key:
        yield v,curr_pos
      if isinstance(v, dict):
        curr_pos.append(k)
        for result in gen_dict_extract(key, v):
          yield result
      elif isinstance(v, list):
        for d in v:
          for result in gen_dict_extract(key, d):
            yield result
    if len(curr_pos)>0:
      curr_pos.pop()


result_list=[]
for ind,i in enumerate(gen_dict_extract('entry12_1',o)):
  result_list.append(i)
  print result_list[-1]
print result_list[-1]
输出:

(12, ['dict1', 'dict12'])
(12, [])

问题是我是一个tuple对象。您需要复制i以避免覆盖

import copy
result_list = []
for in ind in enumerate(gen_dict_extract('entry12_1',o)):
    result_list.append(copy.deepcopy(i))
print result_list

问题是我是一个tuple对象。您需要复制i以避免覆盖

import copy
result_list = []
for in ind in enumerate(gen_dict_extract('entry12_1',o)):
    result_list.append(copy.deepcopy(i))
print result_list

gen\u dict\u extract
中,您使用全局列表
curr\u pos
,并在找到键后直接生成它(
yield v,curr\u pos
)。但是列表是一种可变类型,您可以稍后修改它(
curr\u pos.pop()

您在result_list中存储的只是对全局对象的引用,因此它在循环中包含预期值,但在循环结束时被清空。您应该在收益时间返回一个浅拷贝:
yield v,curr\u pos[:]

然后,您将获得预期的:

(12, ['dict1', 'dict12'])
(12, ['dict1', 'dict12'])

顺便说一句,如果要避免全局列表,可以将该列表作为可选参数传递:

def gen_dict_extract(key, var, curr_pos = None):
    if curr_pos is None:
        curr_pos = []
    ...
        for result in gen_dict_extract(key, v, curr_pos):
    ...
          for result in gen_dict_extract(key, d, curr_pos):
    ...

这将确保您在每次新调用时使用一个新列表,同时在
gen\u dict\u extract
中递归时正确传递它。您使用一个全局列表
curr\u pos
,并在找到键时直接生成它(
yield v,curr\u pos
)。但是列表是一种可变类型,您可以稍后修改它(
curr\u pos.pop()

您在result_list中存储的只是对全局对象的引用,因此它在循环中包含预期值,但在循环结束时被清空。您应该在收益时间返回一个浅拷贝:
yield v,curr\u pos[:]

然后,您将获得预期的:

(12, ['dict1', 'dict12'])
(12, ['dict1', 'dict12'])

顺便说一句,如果要避免全局列表,可以将该列表作为可选参数传递:

def gen_dict_extract(key, var, curr_pos = None):
    if curr_pos is None:
        curr_pos = []
    ...
        for result in gen_dict_extract(key, v, curr_pos):
    ...
          for result in gen_dict_extract(key, d, curr_pos):
    ...

这将确保在每次新调用时都使用一个新列表,同时在递归时正确地传递它

为了完整起见,下面是一个带有Serge建议的版本。此外,我还做了一些额外的更改,以便该函数能够处理任何嵌套列表和dict组合

def gen_dict_extract(key, var,curr_pos=None):
  """
  key: key to search for
  var: nested dict to search in 
  """
  #print curr_pos
  if curr_pos is None:
    curr_pos=[]
  if hasattr(var,'iteritems'):
    for k, v in var.iteritems():
      curr_pos.append(k)
      if k == key:
        yield v,curr_pos[:]
      if isinstance(v, dict):
        for result in gen_dict_extract(key, v,curr_pos):
          yield result
      elif isinstance(v, list):
        curr_pos.append(0)
        for ind,d in enumerate(v):
          curr_pos.pop()
          curr_pos.append(ind)
          for result in gen_dict_extract(key, d,curr_pos):
            yield result
        curr_pos.pop()
      curr_pos.pop()
  elif isinstance(var, list):
    curr_pos.append(0)
    for ind,d in enumerate(var):
      curr_pos.pop()
      curr_pos.append(ind)
      for result in gen_dict_extract(key, d,curr_pos):
        yield result
    curr_pos.pop()

为了完整起见,这里有一个版本包含Serge的建议。此外,我还做了一些额外的更改,以便该函数能够处理任何嵌套列表和dict组合

def gen_dict_extract(key, var,curr_pos=None):
  """
  key: key to search for
  var: nested dict to search in 
  """
  #print curr_pos
  if curr_pos is None:
    curr_pos=[]
  if hasattr(var,'iteritems'):
    for k, v in var.iteritems():
      curr_pos.append(k)
      if k == key:
        yield v,curr_pos[:]
      if isinstance(v, dict):
        for result in gen_dict_extract(key, v,curr_pos):
          yield result
      elif isinstance(v, list):
        curr_pos.append(0)
        for ind,d in enumerate(v):
          curr_pos.pop()
          curr_pos.append(ind)
          for result in gen_dict_extract(key, d,curr_pos):
            yield result
        curr_pos.pop()
      curr_pos.pop()
  elif isinstance(var, list):
    curr_pos.append(0)
    for ind,d in enumerate(var):
      curr_pos.pop()
      curr_pos.append(ind)
      for result in gen_dict_extract(key, d,curr_pos):
        yield result
    curr_pos.pop()

效果很好。任何关于如何避免全球名单的快速想法?都很有效。有没有关于如何避免全球名单的快速想法?