代码之家  ›  专栏  ›  技术社区  ›  cs95 abhishek58g

展平词典时处理自引用

  •  1
  • cs95 abhishek58g  · 技术社区  · 8 年前

    给定一些任意字典

    mydict = {
        'first': {
            'second': {
                'third': {
                    'fourth': 'the end'
                 }
             }
         }
    }
    

    我写了一个小程序,在 writing an answer 另一个问题。

    def recursive_flatten(mydict):
        d = {}
        for k, v in mydict.items():
            if isinstance(v, dict):
                for k2, v2 in recursive_flatten(v).items():
                    d[k + '.' + k2] = v2 
            else:
                d[k] = v
        return d
    

    它起作用了,给了我想要的:

    new_dict = recursive_flatten(mydict)
    
    print(new_dict)
    {'first.second.third.fourth': 'the end'}
    

    应该 适用于任何任意结构的词典。不幸的是,它没有:

    mydict['new_key'] = mydict
    

    现在 recursive_flatten(mydict) 将一直运行,直到堆栈空间用完。我试图弄清楚如何优雅地处理自我引用(基本上,忽略或删除它们)。使事情复杂化的是,任何子词典都可能出现自引用。。。不仅仅是高层。我如何优雅地处理自我介绍?我可以想到一个可变的默认参数,但是 应该 做一个更好的方式。。。正当

    感谢您的阅读。我欢迎任何其他建议/改进 recursive_flatten 如果你有。

    2 回复  |  直到 8 年前
        1
  •  2
  •   Mulan    8 年前

    一种方法是使用 set id 。请注意,此解决方案还使用生成器,这意味着我们可以开始使用展平的dict 之前 计算整个结果

    def recursive_flatten (mydict):
      def loop (seen, path, value):
    
        # if we've seen this value, skip it
        if id(value) in seen:
          return
    
        # if we haven't seen this value, now we have
        else:
          seen.add(id(value))
    
        # if this value is a dict...
        if isinstance (value, dict):
          for (k, v) in value.items ():
            yield from loop(seen, path + [k], v)
    
        # base case
        else:
          yield (".".join(path), value)
    
      # init the loop    
      yield from loop (set(), [], mydict)
    

    程序演示

    mydict = {
        'first': {
            'second': {
                'third': {
                    'fourth': 'the end'
                 }
             }
         }
    }
    
    for (k,v) in recursive_flatten (mydict):
      print (k, v)
    
    # first.second.third.fourth the end
    
    mydict['new_key'] = mydict
    
    for (k,v) in recursive_flatten (mydict):
      print (k, v)
    
    # first.second.third.fourth the end
    

    如果您想查看自参考值的输出,我们可以进行一些轻微的修改

    # if we've seen this value, skip it
    if (id(value) in seen):
      # this is the new line
      yield (".".join(path), "*self-reference* %d" % id(value))
      return
    

    现在程序的输出将是

    first.second.third.fourth the end
    first.second.third.fourth the end
    new_key *self-reference* 139700111853032
    
        2
  •  1
  •   Ryan Haining    8 年前

    我不知道你对“优雅”的定义是什么,但这可以通过对以前在 set 对象ID的数目:

    class RecursiveFlatten:
        def __init__(self):
            self.seen = set()
    
        def __call__(self, mydict):
            self.seen.add(id(mydict))
            d = {}
            for k, v in mydict.items():
                if isinstance(v, dict):
                    if id(v) not in self.seen:
                        self.seen.add(id(v))
                        for k2, v2 in self(v).items():
                            d[k + '.' + k2] = v2
                else:
                    d[k] = v
            return d
    
    def recursive_flatten(mydict):
        return RecursiveFlatten()(mydict)
    

    测试它给了我我所期望的

    mydict = {
        'first': {
            'second': {
                'third': {
                    'fourth': 'the end'
                 }
             },
            'second2': {
                'third2': 'the end2'
            }
         }
    }
    
    mydict['first']['second']['new_key'] = mydict
    mydict['new_key'] = mydict
    print(recursive_flatten(mydict))
    

    输出:

    {'first.second2.third2': 'the end2', 'first.second.third.fourth': 'the end'}